﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Область СлужебныйПрограммныйИнтерфейс

///////////////////////////////////////////////////////////////////////////////
// Основные процедуры и функции.

// Вызывается при входе в систему.
//
// Параметры:
//  РегистрироватьВЖурнале - Булево - если Истина, тогда записать ошибку в журнал регистрации.
//
// Возвращаемое значение:
//  Структура:
//   * ОшибкаАвторизации      - Строка - текст ошибки, если заполнено.
//   * ТребуетсяСменитьПароль - Булево - если Истина, тогда текст ошибки устаревания пароля.
//   
Функция АвторизоватьТекущегоПользователяПриВходе(РегистрироватьВЖурнале) Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("ОшибкаАвторизации", "");
	Результат.Вставить("ТребуетсяСменитьПароль", Ложь);
	
	Попытка
		ОшибкаАвторизации = АвторизоватьТекущегоПользователя(Истина, РегистрироватьВЖурнале);
		
		Если Не ЗначениеЗаполнено(ОшибкаАвторизации) Тогда
			ОтключитьНеактивныхИПросроченныхПользователей(Истина, ОшибкаАвторизации, РегистрироватьВЖурнале);
		КонецЕсли;
		
		Если Не ЗначениеЗаполнено(ОшибкаАвторизации) Тогда
			ПроверитьВходВПрограммуРазрешен(ОшибкаАвторизации);
		КонецЕсли;
		
		Если Не ЗначениеЗаполнено(ОшибкаАвторизации) Тогда
			Результат.ТребуетсяСменитьПароль = ТребуетсяСменитьПароль(ОшибкаАвторизации,
				Истина, РегистрироватьВЖурнале);
		КонецЕсли;
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		ОшибкаАвторизации = КраткоеПредставлениеОшибкиАвторизацииПослеРегистрацииВЖурнале(
			ИнформацияОбОшибке,, РегистрироватьВЖурнале);
		Если РегистрироватьВЖурнале Тогда
			ОшибкаАвторизации = ЗаголовокСообщенияАвторизацияНеВыполненаСПереводомСтроки()
				+ ?(Пользователи.ЭтоПолноправныйПользователь(,, Ложь),
					НСтр("ru = 'Подробнее см. в журнале регистрации.'"),
					НСтр("ru = 'Обратитесь к администратору.'"));
		КонецЕсли;
	КонецПопытки;
	
	Результат.ОшибкаАвторизации = ОшибкаАвторизации;
	
	Возврат Результат;
	
КонецФункции

// Вызывается при начале работы системы, чтобы проверить возможность выполнения авторизации и
// вызвать заполнение значений параметров сеанса ТекущийПользователь и ТекущийВнешнийПользователь.
// Также вызывается при входе в область данных.
//
// Возвращаемое значение:
//  Строка - пустая строка   - авторизация выполнена успешно.
//           Непустая строка - описание ошибки.
//                             При начале работы системы следует
//                             завершить работу 1С:Предприятия.
//
Функция АвторизоватьТекущегоПользователя(ПриНачалеРаботыСистемы = Ложь, РегистрироватьВЖурнале = Ложь) Экспорт
	
	СостояниеДоВызоваАвторизоватьТекущегоПользователя(, Истина);
	
	Если Не ПриНачалеРаботыСистемы Тогда
		ОбновитьПовторноИспользуемыеЗначения();
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	ТекущийПользовательИБ = ПользователиИнформационнойБазы.ТекущийПользователь();
	ЭтоВнешнийПользователь = ЗначениеЗаполнено(Справочники.ВнешниеПользователи.НайтиПоРеквизиту(
		"ИдентификаторПользователяИБ", ТекущийПользовательИБ.УникальныйИдентификатор));
	
	ТекстОшибки = ПроверитьПраваПользователя(ТекущийПользовательИБ,
		"ПриЗапуске", ЭтоВнешнийПользователь, Не ПриНачалеРаботыСистемы, РегистрироватьВЖурнале);
	Если ЗначениеЗаполнено(ТекстОшибки) Тогда
		Возврат ТекстОшибки;
	КонецЕсли;
	
	Если ПустаяСтрока(ТекущийПользовательИБ.Имя) Тогда
		// Авторизуется пользователь по умолчанию.
		Попытка
			Значения = ЗначенияПараметровСеансаТекущийПользователь();
		Исключение
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			ШаблонОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось установить параметр сеанса %1 по причине:
				           |""%2"".
				           |
				           |Обратитесь к администратору.'"),
				"ТекущийПользователь");
			Возврат КраткоеПредставлениеОшибкиАвторизацииПослеРегистрацииВЖурнале(ИнформацияОбОшибке,
				ШаблонОшибки, РегистрироватьВЖурнале);
		КонецПопытки;
		Если ТипЗнч(Значения) = Тип("Строка") Тогда
			Возврат КраткоеПредставлениеОшибкиАвторизацииПослеРегистрацииВЖурнале(Значения, , РегистрироватьВЖурнале);
		КонецЕсли;
		Возврат РезультатУстановкиПараметровСеанса(РегистрироватьВЖурнале);
	КонецЕсли;
	
	НайденныйПользователь = Неопределено;
	ПользовательПоИдентификаторуСуществует(ТекущийПользовательИБ.УникальныйИдентификатор,, НайденныйПользователь);
	
	Если Не ЗначениеЗаполнено(НайденныйПользователь) Тогда
		СтандартнаяОбработка = Истина;
		ИнтеграцияПодсистемБСП.ПриАвторизацииНовогоПользователяИБ(ТекущийПользовательИБ, СтандартнаяОбработка);
		
		Если Не СтандартнаяОбработка Тогда
			Возврат "";
		КонецЕсли;
		ПользовательПоИдентификаторуСуществует(ТекущийПользовательИБ.УникальныйИдентификатор,, НайденныйПользователь);
	КонецЕсли;
	
	Если ЗначениеЗаполнено(НайденныйПользователь) Тогда
		// ПользовательИБ найден в справочнике.
		Если ПриНачалеРаботыСистемы И РолиАдминистратораДоступны() Тогда
			ИнтеграцияПодсистемБСП.ПриСозданииАдминистратора(НайденныйПользователь,
				НСтр("ru = 'При авторизации у пользователя найдены роли администратора.'"));
		КонецЕсли;
		Возврат РезультатУстановкиПараметровСеанса(РегистрироватьВЖурнале);
	КонецЕсли;
	
	// Требуется либо создать администратора, либо сообщить об отказе авторизации.
	ПользователиИБ = ПользователиИнформационнойБазы.ПолучитьПользователей();
	
	Если ПользователиИБ.Количество() > 1
	   И Не РолиАдминистратораДоступны()
	   И Не ПравоДоступа("Администрирование", Метаданные, ТекущийПользовательИБ) Тогда
		
		// Авторизуется обычный пользователь, созданный в конфигураторе.
		Возврат КраткоеПредставлениеОшибкиАвторизацииПослеРегистрацииВЖурнале(
			ТекстСообщенияПользовательНеНайденВСправочнике(ТекущийПользовательИБ.Имя),
			, РегистрироватьВЖурнале);
	КонецЕсли;
	
	// Авторизуется администратор, созданный в конфигураторе.
	Если Не РолиАдминистратораДоступны() Тогда
		Возврат КраткоеПредставлениеОшибкиАвторизацииПослеРегистрацииВЖурнале(
			НСтр("ru = 'Запуск от имени пользователя с правом Администрирование невозможен,
			           |так как он не зарегистрирован в списке пользователей.
			           |
			           |Для ведения списка и настройки прав пользователей предназначен список Пользователи,
			           |режим конфигурирования 1С:Предприятия для этого использовать не следует.'"),
			, РегистрироватьВЖурнале);
	КонецЕсли;
	
	Попытка
		Пользователь = Пользователи.СоздатьАдминистратора(ТекущийПользовательИБ);
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		Возврат КраткоеПредставлениеОшибкиАвторизацииПослеРегистрацииВЖурнале(ИнформацияОбОшибке,
			НСтр("ru = 'Не удалось выполнить автоматическую регистрацию администратора в списке по причине:
			           |""%1"".
			           |
			           |Для ведения списка и настройки прав пользователей предназначен список Пользователи,
			           |режим конфигурирования 1С:Предприятия для этого использовать не следует.'"),
			РегистрироватьВЖурнале);
	КонецПопытки;
	
	Комментарий =
		НСтр("ru = 'Выполнен запуск от имени пользователя с ролью ""Полные права"",
		           |который не зарегистрирован в списке пользователей.
		           |Выполнена автоматическая регистрация в списке пользователей.
		           |
		           |Для ведения списка и настройки прав пользователей предназначен список Пользователи,
		           |режим конфигурирования 1С:Предприятия для этого использовать не следует.'");
	
	ИнтеграцияПодсистемБСП.ПослеЗаписиАдминистратораПриАвторизации(Комментарий);
	
	ЗаписьЖурналаРегистрации(
		НСтр("ru = 'Пользователи.Администратор зарегистрирован в справочнике Пользователи'",
		     ОбщегоНазначения.КодОсновногоЯзыка()),
		УровеньЖурналаРегистрации.Предупреждение,
		Метаданные.Справочники.Пользователи,
		Пользователь,
		Комментарий);
	
	Возврат РезультатУстановкиПараметровСеанса(РегистрироватьВЖурнале);
	
КонецФункции

// Параметры:
//  ПроверитьРолиАдминистратораДоступны - Булево - если истина, вернуть результат
//    с учетом проверки РолиАдминистратораДоступны() для текущего пользователя сеанса.
//  Отключить - Булево - отключает состояние и возвращает Ложь.
// 
// Возвращаемое значение:
//  Булево
//
Функция СостояниеДоВызоваАвторизоватьТекущегоПользователя(ПроверитьРолиАдминистратораДоступны = Ложь,
			Отключить = Ложь) Экспорт
	
	ИмяПараметра = "СостояниеДоВызоваАвторизоватьТекущегоПользователя";
	
	УстановитьПривилегированныйРежим(Истина);
	
	Если ПараметрыСеанса.ПараметрыКлиентаНаСервере.Получить(ИмяПараметра) <> Истина Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если Не Отключить Тогда
		Возврат Не ПроверитьРолиАдминистратораДоступны
			Или РолиАдминистратораДоступны();
	КонецЕсли;
	
	ТекущиеПараметры = Новый Соответствие(ПараметрыСеанса.ПараметрыКлиентаНаСервере);
	ТекущиеПараметры.Удалить("СостояниеДоВызоваАвторизоватьТекущегоПользователя");
	ПараметрыСеанса.ПараметрыКлиентаНаСервере = Новый ФиксированноеСоответствие(ТекущиеПараметры);
	
	Возврат Ложь;
	
КонецФункции

// Определяет, что используется нестандартный способ установки ролей пользователям ИБ.
//
// Возвращаемое значение:
//  Булево
//
Функция ЗапретРедактированияРолей() Экспорт
	
	Возврат ПользователиСлужебныйПовтИсп.Настройки().РедактированиеРолей <> Истина;
	
КонецФункции

// Проверяет, что определяемый тип ВнешнийПользователь
// содержит ссылки на объекты авторизации, а не тип Строка.
//
// Возвращаемое значение:
//  Булево
//
Функция ВнешниеПользователиВнедрены() Экспорт
	
	Возврат ПользователиСлужебныйПовтИсп.ПустыеСсылкиТиповОбъектовАвторизации().Количество() > 0;
	
КонецФункции

// Устанавливает начальные настройки для пользователя информационной базы.
//
// Параметры:
//  ИмяПользователя - Строка - имя пользователя информационной базы, для которого сохраняются настройки.
//  ЭтоВнешнийПользователь - Булево - указать Истина, если пользователь ИБ соответствует внешнему пользователю
//                                    (элементу справочника ВнешниеПользователи).
//
Процедура УстановитьНачальныеНастройки(Знач ИмяПользователя, ЭтоВнешнийПользователь = Ложь) Экспорт
	
	НастройкиКлиента = Новый НастройкиКлиентскогоПриложения;
	НастройкиКлиента.ОтображатьПанелиНавигацииИДействий = Ложь;
	НастройкиКлиента.ОтображатьПанельРазделов = Истина;
	НастройкиКлиента.РежимОткрытияФормПриложения = РежимОткрытияФормПриложения.Закладки;
	НастройкиКлиента.ВариантИнтерфейсаКлиентскогоПриложения = ВариантИнтерфейсаКлиентскогоПриложения.Такси;
	
	НастройкиИнтерфейса = Новый НастройкиКомандногоИнтерфейса;
	НастройкиИнтерфейса.ОтображениеПанелиРазделов = ОтображениеПанелиРазделов.КартинкаИТекст;
	
	НастройкиТакси = Новый НастройкиИнтерфейсаКлиентскогоПриложения;
	НастройкиСостава = Новый НастройкиСоставаИнтерфейсаКлиентскогоПриложения;
	ГруппаСлева = Новый ГруппаНастройкиСоставаИнтерфейсаКлиентскогоПриложения;
	
	ГруппаСлева.Добавить(Новый ЭлементНастройкиСоставаИнтерфейсаКлиентскогоПриложения("ПанельРазделов"));
	НастройкиСостава.Лево.Добавить(ГруппаСлева);
	НастройкиТакси.УстановитьСостав(НастройкиСостава);

	НачальныеНастройки = Новый Структура;
	НачальныеНастройки.Вставить("НастройкиКлиента",    НастройкиКлиента);
	НачальныеНастройки.Вставить("НастройкиИнтерфейса", НастройкиИнтерфейса);
	НачальныеНастройки.Вставить("НастройкиТакси",      НастройкиТакси);
	НачальныеНастройки.Вставить("ЭтоВнешнийПользователь", ЭтоВнешнийПользователь);
	
	ПользователиПереопределяемый.ПриУстановкеНачальныхНастроек(НачальныеНастройки);
	
	Если НачальныеНастройки.НастройкиКлиента <> Неопределено Тогда
		ХранилищеСистемныхНастроек.Сохранить("Общее/НастройкиКлиентскогоПриложения", "",
			НачальныеНастройки.НастройкиКлиента, , ИмяПользователя);
	КонецЕсли;
	
	Если НачальныеНастройки.НастройкиИнтерфейса <> Неопределено Тогда
		ХранилищеСистемныхНастроек.Сохранить("Общее/ПанельРазделов/НастройкиКомандногоИнтерфейса", "",
			НачальныеНастройки.НастройкиИнтерфейса, , ИмяПользователя);
	КонецЕсли;
		
	Если НачальныеНастройки.НастройкиТакси <> Неопределено Тогда
		ХранилищеСистемныхНастроек.Сохранить("Общее/НастройкиИнтерфейсаКлиентскогоПриложения", "",
			НачальныеНастройки.НастройкиТакси, , ИмяПользователя);
	КонецЕсли;
	
КонецПроцедуры

// Возвращает текст ошибки, если у текущего пользователя нет ни роли базовые права, ни ролей администратора.
// Регистрирует ошибку в журнале.
//
// Параметры:
//  РегистрироватьВЖурнале - Булево
//
// Возвращаемое значение:
//  Строка
//
Функция ОшибкаНедостаточноПравДляВходаВПрограмму(РегистрироватьВЖурнале = Истина) Экспорт
	
	// АПК:336-выкл Не заменять на РолиДоступны. Это специальная проверка ролей при входе.
	//@skip-check using-isinrole
	Если РольДоступна(Метаданные.Роли.ПолныеПрава) Тогда
		Возврат "";
	КонецЕсли;
	// АПК:336-вкл
	
	Если Пользователи.ЭтоСеансВнешнегоПользователя() Тогда
		ИмяРолиБазовыеПрава = Метаданные.Роли.БазовыеПраваВнешнихПользователейБСП.Имя;
	Иначе
		ИмяРолиБазовыеПрава = Метаданные.Роли.БазовыеПраваБСП.Имя;
	КонецЕсли;
	
	// АПК:336-выкл Не заменять на РолиДоступны. Это специальная проверка ролей при входе.
	//@skip-check using-isinrole
	Если РольДоступна(ИмяРолиБазовыеПрава) Тогда
		Возврат "";
	КонецЕсли;
	// АПК:336-вкл
	
	Возврат КраткоеПредставлениеОшибкиАвторизацииПослеРегистрацииВЖурнале(
		НСтр("ru = 'Недостаточно прав для входа в программу.
		           |
		           |Обратитесь к администратору.'"),
		, РегистрироватьВЖурнале);
	
КонецФункции

// Только для вызова из процедуры ПроверитьПравоОтключитьЛогикуНачалаРаботыСистемы
// общего модуля СтандартныеПодсистемыВызовСервера.
//
// Возвращаемое значение:
//  Строка - текст ошибки.
//
Функция ОшибкаПроверкиПравТекущегоПользователяПриВходе() Экспорт
	
	Возврат ПроверитьПраваПользователя(ПользователиИнформационнойБазы.ТекущийПользователь(),
		"ПриЗапуске", Пользователи.ЭтоСеансВнешнегоПользователя(), Ложь);
	
КонецФункции

// Создает пользователя <Не указан>.
//
// Возвращаемое значение:
//  СправочникСсылка.Пользователи - ссылка на пользователя <Не указан>.
// 
Функция СоздатьНеуказанногоПользователя() Экспорт
	
	СвойстваНеуказанногоПользователя = СвойстваНеуказанногоПользователя();
	
	Если ОбщегоНазначения.СсылкаСуществует(СвойстваНеуказанногоПользователя.СтандартнаяСсылка) Тогда
		
		Возврат СвойстваНеуказанногоПользователя.СтандартнаяСсылка;
		
	Иначе
		
		НовыйПользователь = Справочники.Пользователи.СоздатьЭлемент();
		НовыйПользователь.Служебный = Истина;
		НовыйПользователь.Наименование = СвойстваНеуказанногоПользователя.ПолноеИмя;
		НовыйПользователь.УстановитьСсылкуНового(СвойстваНеуказанногоПользователя.СтандартнаяСсылка);
		НовыйПользователь.ОбменДанными.Загрузка = Истина;
		НовыйПользователь.Записать();
		
		Возврат НовыйПользователь.Ссылка;
		
	КонецЕсли;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Для работы интерфейса ролей в управляемой форме.

// Только для внутреннего использования.
//
// Параметры:
//  Действие - Строка - НастроитьИнтерфейсРолейПриЧтенииНаСервере,
//                      НастроитьИнтерфейсРолейПриСозданииФормы,
//                      НастроитьИнтерфейсРолейПриЗагрузкеНастроек,
//                      ЗаполнитьРоли,
//                      ОбновитьДеревоРолей,
//                      ОбновитьСоставРолей,
//                      ТолькоВыбранныеРоли,
//                      УстановитьТолькоПросмотрРолей,
//                      ГруппировкаПоПодсистемам.
//
//  Параметры - Структура:
//    * ОсновнойПараметр - Неопределено
//                       - Булево
//                       - Строка
//                       - Массив
//                       - Соответствие
//
//    * Форма - ФормаКлиентскогоПриложения:
//       ** ПоказатьПодсистемыРолей - Булево
//       ** Роли - ДанныеФормыДерево:
//            *** ЭтоРоль - Булево
//            *** Имя     - Строка
//            *** Синоним - Строка
//            *** ЭтоНедоступнаяРоль    - Булево
//            *** ЭтоНесуществующаяРоль - Булево
//            *** Пометка               - Булево
//            *** НомерКартинки         - Число
//       ** Элементы - ВсеЭлементыФормы:
//            *** РолиУстановитьФлажки            - КнопкаФормы
//            *** РолиСнятьФлажки                 - КнопкаФормы
//            *** РолиПоказатьТолькоВыбранныеРоли - КнопкаФормы
//            *** РолиПоказатьПодсистемыРолей     - КнопкаФормы
//
//    * КоллекцияРолей - ТаблицаЗначений:
//        ** Роль - Строка
//    * НазначениеРолей - Строка
//    * СкрытьРольПолныеПрава - Булево
//    * ЗапретИзмененияАдминистративногоДоступа - Булево
//
Процедура ОбработатьИнтерфейсРолей(Действие, Параметры) Экспорт
	
	Если Действие = "УстановитьТолькоПросмотрРолей" Тогда
		УстановитьТолькоПросмотрРолей(Параметры);
		
	ИначеЕсли Действие = "НастроитьИнтерфейсРолейПриЗагрузкеНастроек" Тогда
		НастроитьИнтерфейсРолейПриЗагрузкеНастроек(Параметры);
		
	ИначеЕсли Действие = "НастроитьИнтерфейсРолейПриСозданииФормы" Тогда
		НастроитьИнтерфейсРолейПриСозданииФормы(Параметры);
		
	ИначеЕсли Действие = "НастроитьИнтерфейсРолейПриЧтенииНаСервере" Тогда
		НастроитьИнтерфейсРолейПриЧтенииНаСервере(Параметры);
		
	ИначеЕсли Действие = "ТолькоВыбранныеРоли" Тогда
		ТолькоВыбранныеРоли(Параметры);
		
	ИначеЕсли Действие = "ГруппировкаПоПодсистемам" Тогда
		ГруппировкаПоПодсистемам(Параметры);
		
	ИначеЕсли Действие = "ОбновитьДеревоРолей" Тогда
		ОбновитьДеревоРолей(Параметры);
		
	ИначеЕсли Действие = "ОбновитьСоставРолей" Тогда
		ОбновитьСоставРолей(Параметры);
		
	ИначеЕсли Действие = "ЗаполнитьРоли" Тогда
		ЗаполнитьРоли(Параметры);
	Иначе
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка в процедуре %1
			           |Неверное значение параметра Действие: ""%2"".'"),
			"ПользователиСлужебный.ОбработатьИнтерфейсРолей",
			Действие);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции общего назначения.

// Возвращает имена и синонимы ролей.
//
// Возвращаемое значение:
//  Структура:
//   * Массив - ФиксированныйМассив из Строка - массив имен ролей.
//   * Соответствие - ФиксированноеСоответствие из КлючИЗначение:
//      ** Ключ     - Строка - имя роли.
//      ** Значение - Строка - синоним роли.
//   * Таблица - ХранилищеЗначения из ТаблицаЗначений:
//      ** Имя - Строка - имя роли.
//
Функция ВсеРоли() Экспорт
	
	Возврат ПользователиСлужебныйПовтИсп.ВсеРоли();
	
КонецФункции

// Возвращает недоступные роли для разделенных пользователей или внешних пользователей
// с учетом прав текущего пользователя и режима работы (локальный или модель сервиса).
//
// Параметры:
//  ДляВнешнихПользователей - Булево - если истина, значит для внешних пользователей.
//
// Возвращаемое значение:
//  Соответствие из КлючИЗначение:
//   * Ключ     - Строка - имя роли.
//   * Значение - Булево - Истина.
//
Функция НедоступныеРолиПоТипуПользователя(ДляВнешнихПользователей) Экспорт
	
	Если ДляВнешнихПользователей Тогда
		НазначениеРолейПользователя = "ДляВнешнихПользователей";
		
	ИначеЕсли Не ОбщегоНазначения.РазделениеВключено()
	        И Пользователи.ЭтоПолноправныйПользователь(, Истина) Тогда
		
		// Пользователь с ролью АдминистраторСистемы в локальном режиме работы
		// может выдавать административные права.
		НазначениеРолейПользователя = "ДляАдминистраторов";
	Иначе
		НазначениеРолейПользователя = "ДляПользователей";
	КонецЕсли;
	
	Возврат ПользователиСлужебныйПовтИсп.НедоступныеРоли(НазначениеРолейПользователя);
	
КонецФункции

// Возвращает свойства пользователя для пользователя ИБ с пустым именем.
//
// Возвращаемое значение:
//  Структура:
//    * Ссылка - СправочникСсылка.Пользователи - ссылка на найденный элемент справочника,
//                 соответствующий неуказанному пользователю.
//             - Неопределено - элемент не найден.
//
//    * СтандартнаяСсылка - СправочникСсылка.Пользователи - ссылка, используемая
//                 для поиска и создания неуказанного пользователя в справочнике Пользователи.
//
//    * ПолноеИмя - Строка - полное имя, которое устанавливается в элемент справочника Пользователи
//                    при создании несуществующего неуказанного пользователя.
//
//    * ПолноеИмяДляПоиска - Строка - полное имя, которое используется для поиска
//                  неуказанного пользователя старым способом, необходимым для поддержки
//                  старых версий неуказанного пользователя. Это имя не требуется изменять.
//
Функция СвойстваНеуказанногоПользователя() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Свойства = Новый Структура;
	Свойства.Вставить("Ссылка", Неопределено);
	
	Свойства.Вставить("СтандартнаяСсылка", Справочники.Пользователи.ПолучитьСсылку(
		Новый УникальныйИдентификатор("aa00559e-ad84-4494-88fd-f0826edc46f0")));
	
	Свойства.Вставить("ПолноеИмя", Пользователи.ПолноеИмяНеуказанногоПользователя());
	
	Свойства.Вставить("ПолноеИмяДляПоиска", "<" + НСтр("ru = 'Не указан'") + ">");
	
	// Поиск по уникальному идентификатору.
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Ссылка", Свойства.СтандартнаяСсылка);
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК ЗначениеИстина
	|ИЗ
	|	Справочник.Пользователи КАК Пользователи
	|ГДЕ
	|	Пользователи.Ссылка = &Ссылка";
	
	НачатьТранзакцию();
	Попытка
		Если Запрос.Выполнить().Пустой() Тогда
			Запрос.УстановитьПараметр("ПолноеИмя", Свойства.ПолноеИмяДляПоиска);
			Запрос.Текст =
			"ВЫБРАТЬ ПЕРВЫЕ 1
			|	Пользователи.Ссылка
			|ИЗ
			|	Справочник.Пользователи КАК Пользователи
			|ГДЕ
			|	Пользователи.Наименование = &ПолноеИмя";
			Результат = Запрос.Выполнить();
			
			Если НЕ Результат.Пустой() Тогда
				Выборка = Результат.Выбрать();
				Выборка.Следующий();
				Свойства.Ссылка = Выборка.Ссылка;
			КонецЕсли;
		Иначе
			Свойства.Ссылка = Свойства.СтандартнаяСсылка;
		КонецЕсли;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Возврат Свойства;
	
КонецФункции

// Определяет наличие элемента в справочнике Пользователи
// или справочнике ВнешниеПользователи по уникальному идентификатору
// пользователя информационной.
//  Функция используется для проверки наличия сопоставления пользователяИБ только
// с одним элементом справочников Пользователи и ВнешниеПользователи.
//
// Параметры:
//  УникальныйИдентификатор - УникальныйИдентификатор - идентификатор пользователя ИБ.
//
//  СсылкаНаТекущего - СправочникСсылка.Пользователи
//                   - СправочникСсылка.ВнешниеПользователи - исключить
//                       указанную ссылку из поиска.
//                     Неопределено - искать среди всех элементов справочников.
//
//  НайденныйПользователь - Неопределено - пользователь не существует.
//                        - СправочникСсылка.Пользователи
//                        - СправочникСсылка.ВнешниеПользователи - возвращаемое значение, если найден.
//
//  ИдентификаторПользователяСервиса - Булево
//                     Ложь   - проверять ИдентификаторПользователяИБ.
//                     Истина - проверять ИдентификаторПользователяСервиса.
//
// Возвращаемое значение:
//  Булево
//
Функция ПользовательПоИдентификаторуСуществует(УникальныйИдентификатор,
                                               СсылкаНаТекущего = Неопределено,
                                               НайденныйПользователь = Неопределено,
                                               ИдентификаторПользователяСервиса = Ложь) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("СсылкаНаТекущего", СсылкаНаТекущего);
	Запрос.УстановитьПараметр("УникальныйИдентификатор", УникальныйИдентификатор);
	Запрос.Текст = 
	"ВЫБРАТЬ
	|	Пользователи.Ссылка КАК Пользователь
	|ИЗ
	|	Справочник.Пользователи КАК Пользователи
	|ГДЕ
	|	Пользователи.ИдентификаторПользователяИБ = &УникальныйИдентификатор
	|	И Пользователи.Ссылка <> &СсылкаНаТекущего
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ
	|	ВнешниеПользователи.Ссылка
	|ИЗ
	|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
	|ГДЕ
	|	ВнешниеПользователи.ИдентификаторПользователяИБ = &УникальныйИдентификатор
	|	И ВнешниеПользователи.Ссылка <> &СсылкаНаТекущего";
	
	Результат = Ложь;
	НайденныйПользователь = Неопределено;
	
	РезультатЗапроса = Запрос.Выполнить();
	
	Если Не РезультатЗапроса.Пустой() Тогда
		Выборка = РезультатЗапроса.Выбрать();
		Выборка.Следующий();
		НайденныйПользователь = Выборка.Пользователь;
		Результат = Истина;
		Пользователи.НайтиНеоднозначныхПользователейИБ(Неопределено, УникальныйИдентификатор);
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Параметры:
//  ИдентификаторПользователяИБ - УникальныйИдентификатор
//
Функция ПользовательИБПоИдентификатору(ИдентификаторПользователяИБ) Экспорт
	
	Если ТипЗнч(ИдентификаторПользователяИБ) <> Тип("УникальныйИдентификатор") Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("ТехнологияСервиса.БазоваяФункциональность") Тогда
		МодульРаботаВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("РаботаВМоделиСервиса");
		СеансЗапущенБезРазделителей = МодульРаботаВМоделиСервиса.СеансЗапущенБезРазделителей();
	Иначе
		СеансЗапущенБезРазделителей = Истина;
	КонецЕсли;
	
	Если ОбщегоНазначения.РазделениеВключено()
	   И СеансЗапущенБезРазделителей
	   И ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных()
	   И ИдентификаторПользователяИБ = ПользователиИнформационнойБазы.ТекущийПользователь().УникальныйИдентификатор Тогда
		
		ПользовательИБ = ПользователиИнформационнойБазы.ТекущийПользователь();
	Иначе
		ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(ИдентификаторПользователяИБ);
	КонецЕсли;
	
	Возврат ПользовательИБ;
	
КонецФункции

// Только для внутреннего использования.
//
// Параметры:
//  Форма - ФормаКлиентскогоПриложения
//  ДобавлятьПользователей - Булево
//  ТолькоВнешниеПользователи - Булево
//
Процедура ОбновитьНазначениеПриСозданииНаСервере(Форма, ДобавлятьПользователей = Истина, ТолькоВнешниеПользователи = Ложь) Экспорт
	
	Назначение = Форма.Объект.Назначение;
	
	Если Не ВнешниеПользователи.ИспользоватьВнешнихПользователей() Тогда
		Назначение.Очистить();
		НоваяСтрока = Назначение.Добавить();
		Форма.Элементы.ВыбратьНазначение.Родитель.Видимость = Ложь;
		НоваяСтрока.ТипПользователей = Справочники.Пользователи.ПустаяСсылка();
	КонецЕсли;
	
	Если ДобавлятьПользователей И Назначение.Количество() = 0 Тогда
		Если ТолькоВнешниеПользователи Тогда
			ПустыеСсылки = ПользователиСлужебныйПовтИсп.ПустыеСсылкиТиповОбъектовАвторизации();
			Для Каждого ПустаяСсылка Из ПустыеСсылки Цикл
				НоваяСтрока = Назначение.Добавить();
				НоваяСтрока.ТипПользователей = ПустаяСсылка;
			КонецЦикла;
		Иначе
			НоваяСтрока = Назначение.Добавить();
			НоваяСтрока.ТипПользователей = Справочники.Пользователи.ПустаяСсылка();
		КонецЕсли;
	КонецЕсли;
	
	Если Назначение.Количество() <> 0 Тогда
		МассивПредставлений = Новый Массив;
		Индекс = Назначение.Количество() - 1;
		Пока Индекс >= 0 Цикл
			ТипПользователей = Назначение.Получить(Индекс).ТипПользователей;
			Если ТипПользователей = Неопределено Тогда
				Назначение.Удалить(Индекс);
			Иначе
				МассивПредставлений.Добавить(ТипПользователей.Метаданные().Синоним);
			КонецЕсли;
			Индекс = Индекс - 1;
		КонецЦикла;
		Форма.Элементы.ВыбратьНазначение.Заголовок = СтрСоединить(МассивПредставлений, ", ");
	КонецЕсли;
	
КонецПроцедуры

// Вызывает событие ПередЗаписьюПользователяИБ, проверяет права с учетом
// режима разделения данных и записывает указанного пользователя ИБ.
//
// Параметры:
//  ПользовательИБ  - ПользовательИнформационнойБазы - объект, который требуется записать.
//  ЭтоВнешнийПользователь - Булево - указать Истина, если пользователь ИБ соответствует внешнему пользователю
//                                    (элементу справочника ВнешниеПользователи).
//  Пользователь - Неопределено - найти по уникальному идентификатору пользователя ИБ, если потребуется.
//               - СправочникСсылка.Пользователи
//               - СправочникСсылка.ВнешниеПользователи
//
//  СообщатьВМенеджерСервиса - Булево
//
Процедура ЗаписатьПользователяИнформационнойБазы(ПользовательИБ, ЭтоВнешнийПользователь = Ложь,
			Пользователь = Неопределено, Знач СообщатьВМенеджерСервиса = Истина) Экспорт
	
	ИнтеграцияПодсистемБСП.ПередЗаписьюПользователяИБ(ПользовательИБ);
	
	СообщатьВМенеджерСервиса = СообщатьВМенеджерСервиса
		И ОбщегоНазначения.РазделениеВключено()
		И ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ПользователиВМоделиСервиса");
	
	ЭтоОбновлениеСправочниковПользователей = ПользовательИБ.УникальныйИдентификатор
		= ПараметрыСеанса.ОбновлениеСправочниковПользователей.Получить("ИдентификаторПользователяИБ");
	
	Если СообщатьВМенеджерСервиса И Не ЭтоОбновлениеСправочниковПользователей Тогда
		СтарыйПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
			ПользовательИБ.УникальныйИдентификатор);
	КонецЕсли;
	
	ПроверитьПраваПользователя(ПользовательИБ, "ПередЗаписью", ЭтоВнешнийПользователь);
	ОбновлениеИнформационнойБазыСлужебный.УстановитьФлагОтображенияОписанийДляНовогоПользователя(ПользовательИБ.Имя);
	ПользовательИБ.Записать();
	
	Если Не ЭтоОбновлениеСправочниковПользователей Тогда
		
		ОбновленныйПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
			ПользовательИБ.УникальныйИдентификатор);
		Если ОбновленныйПользовательИБ <> Неопределено Тогда
			ПользовательИБ = ОбновленныйПользовательИБ;
		КонецЕсли;
		
		Если Пользователь = Неопределено Тогда
			Пользователь = Пользователи.НайтиПоИдентификатору(ПользовательИБ.УникальныйИдентификатор);
		КонецЕсли;
		Если Пользователь <> Неопределено Тогда
			РегистрыСведений.СведенияОПользователях.ОбновитьСведенияОПользователе(Пользователь,, ПользовательИБ);
			Если СообщатьВМенеджерСервиса Тогда
				МодульПользователиСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ПользователиСлужебныйВМоделиСервиса");
				МодульПользователиСлужебныйВМоделиСервиса.СообщитьИзмененЗапускПриложения(Пользователь,
					ПользовательИБ, СтарыйПользовательИБ);
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Проверяет корректность заполнения назначения ролей, а также корректность прав в ролях по назначению.
//
// Параметры:
//  НазначениеРолей - Неопределено
//  ПроверитьВсе - Булево
//  СписокОшибок - Неопределено
//               - СписокЗначений - найденные ошибки добавляются в список без вызова исключения, где:
//                   * Значение      - Строка - имя роли.
//                                   - Неопределено - роль, указанная в процедуре, не существует в метаданных.
//                   * Представление - Строка - текст ошибки.
//
Процедура ПроверитьНазначениеРолей(НазначениеРолей = Неопределено, ПроверитьВсе = Ложь, СписокОшибок = Неопределено) Экспорт
	
	Если НазначениеРолей = Неопределено Тогда
		НазначениеРолей = ПользователиСлужебныйПовтИсп.НазначениеРолей();
	КонецЕсли;
	
	ЗаголовокОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Ошибка в процедуре %1 общего модуля %2.'"),
		"ПриОпределенииНазначенияРолей",
		"ПользователиПереопределяемый");
	
	ТекстОшибки = "";
	
	Назначение = НазначениеРолей();
	Для Каждого ОписаниеНазначенияРолей Из НазначениеРолей Цикл
		Роли = Новый Соответствие;
		Для Каждого КлючИЗначение Из ОписаниеНазначенияРолей.Значение Цикл
			Роль = Метаданные.Роли.Найти(КлючИЗначение.Ключ);
			Если Роль = Неопределено Тогда
				ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'В метаданных не существует роль ""%1"",
						           |указанная в назначении %2.'"),
						КлючИЗначение.Ключ, ОписаниеНазначенияРолей.Ключ);
				Если СписокОшибок = Неопределено Тогда
					ТекстОшибки = ТекстОшибки + Символы.ПС + Символы.ПС + ОписаниеОшибки;
				Иначе
					СписокОшибок.Добавить(Неопределено, ОписаниеОшибки);
				КонецЕсли;
				Продолжить;
			КонецЕсли;
			Роли.Вставить(Роль, Истина);
			Для Каждого ОписаниеНазначения Из Назначение Цикл
				ТекущиеРоли = ОписаниеНазначения.Значение; // Соответствие
				Если ТекущиеРоли.Получить(Роль) = Неопределено Тогда
					Продолжить;
				КонецЕсли;
				ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Роль ""%1"" указана более чем в одном назначении:
					           |%2, %3.'"),
					Роль.Имя, ОписаниеНазначенияРолей.Ключ, ОписаниеНазначения.Ключ);
				Если СписокОшибок = Неопределено Тогда
					ТекстОшибки = ТекстОшибки + Символы.ПС + Символы.ПС + ОписаниеОшибки;
				Иначе
					СписокОшибок.Добавить(Роль, ОписаниеОшибки);
				КонецЕсли;
			КонецЦикла;
		КонецЦикла;
		Назначение.Вставить(ОписаниеНазначенияРолей.Ключ, Роли);
	КонецЦикла;
	
	// Проверка ролей внешних пользователей.
	НедоступныеПрава = Новый Массив;
	НедоступныеПрава.Добавить("Администрирование");
	НедоступныеПрава.Добавить("АдминистрированиеРасширенийКонфигурации");
	НедоступныеПрава.Добавить("ОбновлениеКонфигурацииБазыДанных");
	НедоступныеПрава.Добавить("АдминистрированиеДанных");
	
	ПроверитьСоставПравРолей(НедоступныеПрава, Назначение.ТолькоДляВнешнихПользователей, ТекстОшибки,
		НСтр("ru = 'При проверке ролей только для внешних пользователей найдены ошибки:'"), СписокОшибок);
	
	ПроверитьСоставПравРолей(НедоступныеПрава, Назначение.СовместноДляПользователейИВнешнихПользователей, ТекстОшибки,
		НСтр("ru = 'При проверке ролей совместно для пользователей и внешних пользователей найдены ошибки:'"), СписокОшибок);
	
	// Проверка ролей пользователей.
	Если ОбщегоНазначения.РазделениеВключено() Или ПроверитьВсе Тогда
		Роли = Новый Соответствие;
		Для Каждого Роль Из Метаданные.Роли Цикл
			Если Назначение.ТолькоДляАдминистраторовСистемы.Получить(Роль) <> Неопределено
			 Или Назначение.ТолькоДляПользователейСистемы.Получить(Роль) <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Роли.Вставить(Роль, Истина);
		КонецЦикла;
		НедоступныеПрава = Новый Массив;
		НедоступныеПрава.Добавить("Администрирование");
		НедоступныеПрава.Добавить("АдминистрированиеРасширенийКонфигурации");
		НедоступныеПрава.Добавить("ОбновлениеКонфигурацииБазыДанных");
		НедоступныеПрава.Добавить("ТолстыйКлиент");
		НедоступныеПрава.Добавить("ВнешнееСоединение");
		НедоступныеПрава.Добавить("Automation");
		НедоступныеПрава.Добавить("ИнтерактивноеОткрытиеВнешнихОбработок");
		НедоступныеПрава.Добавить("ИнтерактивноеОткрытиеВнешнихОтчетов");
		НедоступныеПрава.Добавить("РежимВсеФункции");
		
		НеразделенныеДанные = НеразделенныеДанные();
		ПроверитьСоставПравРолей(НедоступныеПрава, Роли, ТекстОшибки,
			НСтр("ru = 'При проверке ролей для пользователей приложения найдены ошибки:'"), СписокОшибок, НеразделенныеДанные);
	КонецЕсли;
	Если Не ОбщегоНазначения.РазделениеВключено() Или ПроверитьВсе Тогда
		Роли = Новый Соответствие;
		Для Каждого Роль Из Метаданные.Роли Цикл
			Если Назначение.ТолькоДляАдминистраторовСистемы.Получить(Роль) <> Неопределено
			 Или Назначение.ТолькоДляВнешнихПользователей.Получить(Роль) <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Роли.Вставить(Роль, Истина);
		КонецЦикла;
		НедоступныеПрава = Новый Массив;
		НедоступныеПрава.Добавить("Администрирование");
		НедоступныеПрава.Добавить("АдминистрированиеРасширенийКонфигурации");
		НедоступныеПрава.Добавить("ОбновлениеКонфигурацииБазыДанных");
		
		ПроверитьСоставПравРолей(НедоступныеПрава, Роли, ТекстОшибки,
			НСтр("ru = 'При проверке ролей для пользователей найдены ошибки:'"), СписокОшибок);
		
		ПроверитьСоставПравРолей(НедоступныеПрава, Назначение.СовместноДляПользователейИВнешнихПользователей, ТекстОшибки,
			НСтр("ru = 'При проверке ролей совместно для пользователей и внешних пользователей найдены ошибки:'"), СписокОшибок);
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ТекстОшибки) Тогда
		ВызватьИсключение ЗаголовокОшибки + ТекстОшибки;
	КонецЕсли;
	
КонецПроцедуры

// Включает пользователя приемника в группы пользователей пользователя источника.
// Вызывается из обработчика формы ПриЗаписиНаСервере.
//
Процедура СкопироватьГруппыПользователя(Источник, Приемник) Экспорт
	
	ВнешнийПользователь = (ТипЗнч(Источник) = Тип("СправочникСсылка.ВнешниеПользователи"));
	
	Запрос = Новый Запрос;
	Блокировка = Новый БлокировкаДанных;
	
	Если ВнешнийПользователь Тогда
		ЭлементБлокировки = Блокировка.Добавить("Справочник.ГруппыВнешнихПользователей");
		Запрос.Текст = 
			"ВЫБРАТЬ
			|	ГруппыПользователейСостав.Ссылка КАК ГруппаПользователей
			|ИЗ
			|	Справочник.ГруппыВнешнихПользователей.Состав КАК ГруппыПользователейСостав
			|ГДЕ
			|	ГруппыПользователейСостав.ВнешнийПользователь = &Пользователь";
	Иначе
		ЭлементБлокировки = Блокировка.Добавить("Справочник.ГруппыПользователей");
		Запрос.Текст = 
			"ВЫБРАТЬ
			|	ГруппыПользователейСостав.Ссылка КАК ГруппаПользователей
			|ИЗ
			|	Справочник.ГруппыПользователей.Состав КАК ГруппыПользователейСостав
			|ГДЕ
			|	ГруппыПользователейСостав.Пользователь = &Пользователь";
	КонецЕсли;
	Запрос.УстановитьПараметр("Пользователь", Источник);
	Запрос.УстановитьПараметр("Приемник", Приемник);
	РезультатЗапроса = Запрос.Выполнить();
	Выборка = РезультатЗапроса.Выбрать();
	
	ЭлементБлокировки.ИсточникДанных = РезультатЗапроса;
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		
		Пока Выборка.Следующий() Цикл
			ГруппаПользователейОбъект = Выборка.ГруппаПользователей.ПолучитьОбъект(); // СправочникОбъект.ГруппыПользователей, СправочникОбъект.ГруппыВнешнихПользователей
			Отбор = Новый Структура;
			Отбор.Вставить(?(ВнешнийПользователь, "ВнешнийПользователь", "Пользователь"), Приемник);
			НайденныеСтроки = ГруппаПользователейОбъект.Состав.НайтиСтроки(Отбор);
			Если НайденныеСтроки.Количество() <> 0 Тогда
				Продолжить;
			КонецЕсли;
			
			Строка = ГруппаПользователейОбъект.Состав.Добавить();
			Если ВнешнийПользователь Тогда
				Строка.ВнешнийПользователь = Приемник;
			Иначе
				Строка.Пользователь = Приемник;
			КонецЕсли;
			
			ГруппаПользователейОбъект.Записать();
		КонецЦикла;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Возвращает описание контактной информации пользователя.
// Например, адрес электронной почты и телефон.
//
// Параметры:
//   Пользователь - СправочникСсылка.Пользователи
//
// Возвращаемое значение:
//   Структура:
//   * Наименование - Строка
//   * ИдентификаторПользователяИБ - Строка
//   * Фотография - ДвоичныеДанные
// 	             - Неопределено
//   * Недействителен - Строка
//   * ПометкаУдаления - Строка
//   * Телефон - Строка
//   * АдресЭлектроннойПочты - Строка
//   * Подразделение - Неопределено
// 	                - ОпределяемыйТип.Подразделение
//
Функция ОписаниеПользователя(Пользователь) Экспорт

	Результат = Новый Структура;
	Результат.Вставить("Наименование", "");
	Результат.Вставить("ИдентификаторПользователяИБ", "");
	Результат.Вставить("Фотография");
	Результат.Вставить("Подразделение");
	Результат.Вставить("Недействителен", Истина);
	Результат.Вставить("ПометкаУдаления", Истина);
	Результат.Вставить("Телефон", "");
	Результат.Вставить("АдресЭлектроннойПочты", "");
	
	СвойстваПользователя = ?(ТипЗнч(Пользователь) = Тип("СправочникОбъект.Пользователи"),
		Пользователь,
		ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Пользователь,
			" Наименование,
			| ИдентификаторПользователяИБ,
			| Фотография,
			| ПометкаУдаления,
			| Недействителен,
			| Подразделение"));
	ЗаполнитьЗначенияСвойств(Результат, СвойстваПользователя);
	Результат.Фотография = ?(СвойстваПользователя.Фотография = Неопределено, Неопределено, СвойстваПользователя.Фотография.Получить());
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.КонтактнаяИнформация") Тогда
		МодульУправлениеКонтактнойИнформацией = ОбщегоНазначения.ОбщийМодуль("УправлениеКонтактнойИнформацией");
		ЗначениеКонтактнойИнформации = МодульУправлениеКонтактнойИнформацией.КонтактнаяИнформацияОбъекта(Пользователь,,,Ложь);
		
		ВидТелефон = МодульУправлениеКонтактнойИнформацией.ВидКонтактнойИнформацииПоИмени("ТелефонПользователя");
		ВидАдресПочты = МодульУправлениеКонтактнойИнформацией.ВидКонтактнойИнформацииПоИмени("EmailПользователя");
		Для каждого Контакт Из ЗначениеКонтактнойИнформации Цикл
			
			Если Контакт.Вид = ВидТелефон И НЕ ЗначениеЗаполнено(Результат.Телефон) Тогда
				
				Результат.Телефон = Контакт.Представление;
			ИначеЕсли Контакт.Вид = ВидАдресПочты И НЕ ЗначениеЗаполнено(Результат.АдресЭлектроннойПочты) Тогда
				
				Результат.АдресЭлектроннойПочты = Контакт.Представление;
			КонецЕсли;
			
		КонецЦикла;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(Результат.ИдентификаторПользователяИБ) Тогда
		Если ТипЗнч(Пользователь) = Тип("СправочникОбъект.Пользователи") Тогда
			ПользовательСсылка = Пользователь.Ссылка;
		КонецЕсли;
		Если ПользовательСсылка = Пользователи.СсылкаНеуказанногоПользователя() Тогда
			Результат.ИдентификаторПользователяИБ = ПользователиИнформационнойБазы.НайтиПоИмени("").УникальныйИдентификатор;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Результат;

КонецФункции

// Параметры:
//  Список - ДинамическийСписок
//
Процедура НастроитьПолеНомерКартинкиДинамическогоСписка(Список) Экспорт
	
	ОграничитьИспользованиеЗаполняемогоПоляДинамическогоСписка(Список, "НомерКартинки");
	
КонецПроцедуры

// Параметры:
//  ИмяЭлемента - Строка
//  Настройки - НастройкиКомпоновкиДанных
//  Строки - СтрокиДинамическогоСписка
//
Процедура ДинамическийСписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки) Экспорт
	
	Если Строки.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Для Каждого КлючИЗначение Из Строки Цикл
		Если Не КлючИЗначение.Значение.Данные.Свойство("НомерКартинки")
		 Или ТипЗнч(КлючИЗначение.Ключ) <> Тип("СправочникСсылка.Пользователи")
		   И ТипЗнч(КлючИЗначение.Ключ) <> Тип("СправочникСсылка.ВнешниеПользователи") Тогда
			Возврат;
		КонецЕсли;
		Прервать;
	КонецЦикла;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Пользователи", Строки.ПолучитьКлючи());
	Запрос.Текст =
	"ВЫБРАТЬ
	|	СведенияОПользователях.Пользователь КАК Пользователь,
	|	СведенияОПользователях.НомерКартинкиСостояния - 1 КАК НомерКартинки
	|ИЗ
	|	РегистрСведений.СведенияОПользователях КАК СведенияОПользователях
	|ГДЕ
	|	СведенияОПользователях.Пользователь В (&Пользователи)";
	
	УстановитьПривилегированныйРежим(Истина);
	Выборка = Запрос.Выполнить().Выбрать();
	УстановитьПривилегированныйРежим(Ложь);
	
	Пока Выборка.Следующий() Цикл
		Строка = Строки.Получить(Выборка.Пользователь);
		Строка.Данные.НомерКартинки = Выборка.НомерКартинки;
	КонецЦикла;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Универсальные процедуры и функции.

// Возвращает ссылку старого объекта или ссылку нового объекта.
//
// Параметры:
//  Объект   - СправочникОбъект.Пользователи
//           - СправочникОбъект.ВнешниеПользователи
//
//  ЭтоНовый - Булево - возвращаемое значение.
//
// Возвращаемое значение:
//  СправочникСсылка.Пользователи
//  СправочникСсылка.ВнешниеПользователи
//
Функция СсылкаОбъекта(Знач Объект, ЭтоНовый = Неопределено) Экспорт
	
	Ссылка = Объект.Ссылка;
	ЭтоНовый = НЕ ЗначениеЗаполнено(Ссылка);
	
	Если ЭтоНовый Тогда
		Ссылка = Объект.ПолучитьСсылкуНового();
		
		Если НЕ ЗначениеЗаполнено(Ссылка) Тогда
			
			Менеджер = ОбщегоНазначения.МенеджерОбъектаПоСсылке(Объект.Ссылка);
			Ссылка = Менеджер.ПолучитьСсылку();
			Объект.УстановитьСсылкуНового(Ссылка);
		КонецЕсли;
	КонецЕсли;
	
	Возврат Ссылка;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Обработчики событий подсистем конфигурации.

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииПараметровРаботыКлиентаПриЗапуске.
Процедура ПриДобавленииПараметровРаботыКлиентаПриЗапуске(Параметры, Отказ, ЭтоВызовПередНачаломРаботыСистемы) Экспорт
	
	Если Не ЭтоВызовПередНачаломРаботыСистемы Тогда
		КлючПредупрежденияБезопасности = КлючПредупрежденияБезопасностиПриЗапуске();
		Если ЗначениеЗаполнено(КлючПредупрежденияБезопасности) Тогда
			Параметры.Вставить("КлючПредупрежденияБезопасности", КлючПредупрежденияБезопасности);
		КонецЕсли;
		Возврат;
	КонецЕсли;
	
	Если Параметры.ПолученныеПараметрыКлиента.Свойство("АвторизацияВыполнена") Тогда
		РезультатАвторизации = Параметры.ПолученныеПараметрыКлиента.АвторизацияВыполнена;
	Иначе
		РезультатАвторизации = Новый Структура;
		
		Результат = АвторизоватьТекущегоПользователяПриВходе(Истина);
		
		Если ЗначениеЗаполнено(Результат.ОшибкаАвторизации) Тогда
			РезультатАвторизации.Вставить("ОшибкаАвторизации", Результат.ОшибкаАвторизации);
			
		Иначе
			Если Результат.ТребуетсяСменитьПароль Тогда
				РезультатАвторизации.Вставить("ТребуетсяСменитьПароль");
				СтандартныеПодсистемыВызовСервера.СкрытьРабочийСтолПриНачалеРаботыСистемы();
			КонецЕсли;
			
			Если Не ОбщегоНазначения.РазделениеВключено()
			   И Пользователи.ЭтоПолноправныйПользователь(, Истина, Ложь)
			   И РегистрыСведений.СведенияОПользователях.ЗадатьВопросПроОтключениеOpenIDConnect() Тогда
			
				РезультатАвторизации.Вставить("ЗадатьВопросПроОтключениеOpenIDConnect");
			КонецЕсли;
		КонецЕсли;
		
		Параметры.ПолученныеПараметрыКлиента.Вставить("АвторизацияВыполнена",
			?(ЗначениеЗаполнено(РезультатАвторизации), РезультатАвторизации, Неопределено));
	КонецЕсли;
	
	Если ЗначениеЗаполнено(РезультатАвторизации) Тогда
		Для Каждого КлючИЗначение Из РезультатАвторизации Цикл
			Параметры.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
		КонецЦикла;
	КонецЕсли;
	
	Если Параметры.Свойство("ОшибкаАвторизации") Тогда
		Отказ = Истина;
	КонецЕсли;
	
КонецПроцедуры

// См. ГрупповоеИзменениеОбъектовПереопределяемый.ПриОпределенииОбъектовСРедактируемымиРеквизитами.
Процедура ПриОпределенииОбъектовСРедактируемымиРеквизитами(Объекты) Экспорт
	Объекты.Вставить(Метаданные.Справочники.ВнешниеПользователи.ПолноеИмя(), "РеквизитыНеРедактируемыеВГрупповойОбработке");
	Объекты.Вставить(Метаданные.Справочники.ГруппыВнешнихПользователей.ПолноеИмя(), "РеквизитыНеРедактируемыеВГрупповойОбработке");
	Объекты.Вставить(Метаданные.Справочники.Пользователи.ПолноеИмя(), "РеквизитыНеРедактируемыеВГрупповойОбработке");
КонецПроцедуры

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииОбработчиковУстановкиПараметровСеанса.
Процедура ПриДобавленииОбработчиковУстановкиПараметровСеанса(Обработчики) Экспорт
	
	Обработчики.Вставить("ТекущийПользователь",        "ПользователиСлужебный.УстановкаПараметровСеанса");
	Обработчики.Вставить("ТекущийВнешнийПользователь", "ПользователиСлужебный.УстановкаПараметровСеанса");
	Обработчики.Вставить("АвторизованныйПользователь", "ПользователиСлужебный.УстановкаПараметровСеанса");
	Обработчики.Вставить("ОбновлениеСправочниковПользователей", "ПользователиСлужебный.УстановкаПараметровСеанса");
	
КонецПроцедуры

// См. УправлениеДоступомПереопределяемый.ПриЗаполненииВидовДоступа
Процедура ПриЗаполненииВидовДоступа(ВидыДоступа) Экспорт
	
	ВидДоступа = ВидыДоступа.Добавить();
	ВидДоступа.Имя                    = "Пользователи";
	ВидДоступа.Представление          = НСтр("ru = 'Пользователи'");
	ВидДоступа.ТипЗначений            = Тип("СправочникСсылка.Пользователи");
	ВидДоступа.ТипГруппЗначений       = Тип("СправочникСсылка.ГруппыПользователей");
	ВидДоступа.НесколькоГруппЗначений = Истина; // Должно быть Истина, особый случай.
	
	ВидДоступа = ВидыДоступа.Добавить();
	ВидДоступа.Имя                    = "ВнешниеПользователи";
	ВидДоступа.Представление          = НСтр("ru = 'Внешние пользователи'");
	ВидДоступа.ТипЗначений            = Тип("СправочникСсылка.ВнешниеПользователи");
	ВидДоступа.ТипГруппЗначений       = Тип("СправочникСсылка.ГруппыВнешнихПользователей");
	ВидДоступа.НесколькоГруппЗначений = Истина; // Должно быть Истина, особый случай.
	
КонецПроцедуры

// См. УправлениеДоступомПереопределяемый.ПриЗаполненииСписковСОграничениемДоступа.
Процедура ПриЗаполненииСписковСОграничениемДоступа(Списки) Экспорт
	
	Списки.Вставить(Метаданные.Справочники.ВнешниеПользователи, Истина);
	Списки.Вставить(Метаданные.Справочники.ГруппыВнешнихПользователей, Истина);
	Списки.Вставить(Метаданные.Справочники.Пользователи, Истина);
	
КонецПроцедуры

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииСерверныхОповещений
Процедура ПриДобавленииСерверныхОповещений(Оповещения) Экспорт
	
	Если ОбщегоНазначения.РазделениеВключено()
	   И ОбщегоНазначения.ПодсистемаСуществует("ТехнологияСервиса.БазоваяФункциональность") Тогда
		
		МодульРаботаВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("РаботаВМоделиСервиса");
		Если МодульРаботаВМоделиСервиса.СеансЗапущенБезРазделителей() Тогда
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	Оповещение = СерверныеОповещения.НовоеСерверноеОповещение(
		"СтандартныеПодсистемы.Пользователи.ИзменениеРолейПользователяИлиБлокировка");
	
	Оповещение.ИмяМодуляОтправки  = "ПользователиСлужебный";
	Оповещение.ИмяМодуляПолучения = "ПользователиСлужебныйКлиент";
	Оповещение.Параметры = ХешСуммаРолейПользователяИБ(
		ПользователиИнформационнойБазы.ТекущийПользователь());
	
	Оповещения.Вставить(Оповещение.Имя, Оповещение);
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПриОтправкеСерверногоОповещения
Процедура ПриОтправкеСерверногоОповещения(ИмяОповещения, ВариантыПараметров) Экспорт
	
	ОбновитьДатуПоследнейАктивностиПользователей(ВариантыПараметров);
	ОтключитьНеактивныхИПросроченныхПользователей();
	
	АдресатыБлокировки = Новый Соответствие;
	АдресатыИзмененияРолей = Новый Соответствие;
	
	Для Каждого ВариантПараметров Из ВариантыПараметров Цикл
		Для Каждого Адресат Из ВариантПараметров.Адресаты Цикл
			ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(Адресат.Ключ);
			Если ПользовательИБ = Неопределено
			 Или ПользовательИБ.АутентификацияСтандартная    = Ложь
			   И ПользовательИБ.АутентификацияOpenID         = Ложь
			   И ПользовательИБ.АутентификацияOpenIDConnect  = Ложь
			   И ПользовательИБ.АутентификацияТокеномДоступа = Ложь
			   И ПользовательИБ.АутентификацияОС             = Ложь Тогда
			
				АдресатыБлокировки.Вставить(Адресат.Ключ, Адресат.Значение);
				Продолжить;
			КонецЕсли;
			НоваяХешСумма = ХешСуммаРолейПользователяИБ(ПользовательИБ);
			Если ВариантПараметров.Параметры <> НоваяХешСумма Тогда
				ДатаНапомнитьЗавтра = ОбщегоНазначения.ХранилищеСистемныхНастроекЗагрузить(
					"КонтрольИзмененияРолейПользователяИБ", "ДатаНапомнитьЗавтра",,, ПользовательИБ.Имя);
				Если ТипЗнч(ДатаНапомнитьЗавтра) = Тип("Дата")
				   И ТекущаяДатаСеанса() < ДатаНапомнитьЗавтра Тогда
					Продолжить;
				КонецЕсли;
				АдресатыИзмененияРолей.Вставить(Адресат.Ключ, Адресат.Значение);
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	Если ЗначениеЗаполнено(АдресатыБлокировки) Тогда
		СерверныеОповещения.ОтправитьСерверноеОповещение(ИмяОповещения, "ВходВПрограммуЗапрещен", АдресатыБлокировки);
	КонецЕсли;
	Если ЗначениеЗаполнено(АдресатыИзмененияРолей) Тогда
		СерверныеОповещения.ОтправитьСерверноеОповещение(ИмяОповещения, "РолиИзменены", АдресатыИзмененияРолей);
	КонецЕсли;
	
КонецПроцедуры

// См. ЗагрузкаДанныхИзФайлаПереопределяемый.ПриОпределенииСправочниковДляЗагрузкиДанных.
Процедура ПриОпределенииСправочниковДляЗагрузкиДанных(ЗагружаемыеСправочники) Экспорт
	
	// Загрузка в справочник ВнешниеПользователи запрещена.
	СтрокаТаблицы = ЗагружаемыеСправочники.Найти(Метаданные.Справочники.ВнешниеПользователи.ПолноеИмя(), "ПолноеИмя");
	Если СтрокаТаблицы <> Неопределено Тогда 
		ЗагружаемыеСправочники.Удалить(СтрокаТаблицы);
	КонецЕсли;
	
	// Загрузка в справочник Пользователи запрещена.
	СтрокаТаблицы = ЗагружаемыеСправочники.Найти(Метаданные.Справочники.Пользователи.ПолноеИмя(), "ПолноеИмя");
	Если СтрокаТаблицы <> Неопределено Тогда 
		ЗагружаемыеСправочники.Удалить(СтрокаТаблицы);
	КонецЕсли;

	
КонецПроцедуры

// См. ЦентрМониторингаПереопределяемый.ПриСбореПоказателейСтатистикиКонфигурации.
Процедура ПриСбореПоказателейСтатистикиКонфигурации() Экспорт
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЦентрМониторинга") Тогда
		Возврат;
	КонецЕсли;
	
	МодульЦентрМониторинга = ОбщегоНазначения.ОбщийМодуль("ЦентрМониторинга");
	
	АутентификацияСтандартная = 0;
	АутентификацияOpenID = 0;
	АутентификацияOpenIDConnect = 0;
	АутентификацияТокеномДоступа = 0;
	АутентификацияОС = 0;
	ВходВПрограммуРазрешен = 0;
	Для Каждого ОписаниеПользователя Из ПользователиИнформационнойБазы.ПолучитьПользователей() Цикл
		АутентификацияСтандартная = АутентификацияСтандартная
			+ ?(ОписаниеПользователя.АутентификацияСтандартная, 1, 0);
		АутентификацияOpenID = АутентификацияOpenID
			+ ?(ОписаниеПользователя.АутентификацияOpenID, 1, 0);
		АутентификацияOpenIDConnect = АутентификацияOpenIDConnect
			+ ?(ОписаниеПользователя.АутентификацияOpenIDConnect, 1, 0);
		АутентификацияТокеномДоступа = АутентификацияТокеномДоступа
			+ ?(ОписаниеПользователя.АутентификацияТокеномДоступа, 1, 0);
		АутентификацияОС = АутентификацияОС
			+ ?(ОписаниеПользователя.АутентификацияОС, 1, 0);
		ВходВПрограммуРазрешен = ВходВПрограммуРазрешен
			+ ?(ОписаниеПользователя.АутентификацияСтандартная
				Или ОписаниеПользователя.АутентификацияOpenID
				Или ОписаниеПользователя.АутентификацияOpenIDConnect
				Или ОписаниеПользователя.АутентификацияТокеномДоступа
				Или ОписаниеПользователя.АутентификацияОС, 1, 0);
	КонецЦикла;
	
	МодульЦентрМониторинга.ЗаписатьСтатистикуОбъектаКонфигурации(
		"Справочник.Пользователи.АутентификацияСтандартная", АутентификацияСтандартная);
	
	МодульЦентрМониторинга.ЗаписатьСтатистикуОбъектаКонфигурации(
		"Справочник.Пользователи.АутентификацияOpenID", АутентификацияOpenID);
	
	МодульЦентрМониторинга.ЗаписатьСтатистикуОбъектаКонфигурации(
		"Справочник.Пользователи.АутентификацияOpenIDConnect", АутентификацияOpenIDConnect);
	
	МодульЦентрМониторинга.ЗаписатьСтатистикуОбъектаКонфигурации(
		"Справочник.Пользователи.АутентификацияТокеномДоступа", АутентификацияТокеномДоступа);
	
	МодульЦентрМониторинга.ЗаписатьСтатистикуОбъектаКонфигурации(
		"Справочник.Пользователи.АутентификацияОС", АутентификацияОС);
	
	МодульЦентрМониторинга.ЗаписатьСтатистикуОбъектаКонфигурации(
		"Справочник.Пользователи.ВходВПрограммуРазрешен", ВходВПрограммуРазрешен);

	ТекстЗапроса = 
	"ВЫБРАТЬ
	|	КОЛИЧЕСТВО(1) КАК Количество
	|ИЗ
	|	Справочник.Пользователи КАК Пользователи
	|ГДЕ
	|	Пользователи.Недействителен";
	
	Запрос = Новый Запрос(ТекстЗапроса);
	Выборка = Запрос.Выполнить().Выбрать();
	Выборка.Следующий();
	
	МодульЦентрМониторинга.ЗаписатьСтатистикуОбъектаКонфигурации(
		"Справочник.Пользователи.Недействителен", Выборка.Количество);
	
	Настройки = НастройкиВхода().Пользователи;
	ИспользованиеРасширенныхНастроекВхода = Настройки.ПарольДолженОтвечатьТребованиямСложности
		Или ЗначениеЗаполнено(Настройки.МинимальнаяДлинаПароля)
		Или ЗначениеЗаполнено(Настройки.МаксимальныйСрокДействияПароля)
		Или ЗначениеЗаполнено(Настройки.МинимальныйСрокДействияПароля)
		Или ЗначениеЗаполнено(Настройки.ЗапретитьПовторениеПароляСредиПоследних)
		Или ЗначениеЗаполнено(Настройки.ПредупреждатьОбОкончанииСрокаДействияПароля)
		Или ЗначениеЗаполнено(Настройки.ПросрочкаРаботыВПрограммеДоЗапрещенияВхода);
	
	МодульЦентрМониторинга.ЗаписатьСтатистикуОбъектаКонфигурации(
		"Справочник.Пользователи.ИспользованиеРасширенныхНастроекВхода",
		ИспользованиеРасширенныхНастроекВхода);
	
	ТекстЗапроса = 
	"ВЫБРАТЬ
	|	КОЛИЧЕСТВО(1) КАК Количество
	|ИЗ
	|	РегистрСведений.СведенияОПользователях КАК СведенияОПользователях
	|ГДЕ
	|	СведенияОПользователях.ДатаПоследнейАктивности >= &ДатаСреза";
	
	Запрос = Новый Запрос(ТекстЗапроса);
	Запрос.УстановитьПараметр("ДатаСреза", НачалоДня(ТекущаяДатаСеанса() - 30 *60*60*24)); // 30 дней.
	Выборка = Запрос.Выполнить().Выбрать();
	Выборка.Следующий();
	
	МодульЦентрМониторинга.ЗаписатьСтатистикуОбъектаКонфигурации(
		"Справочник.Пользователи.Активный", Выборка.Количество);
	
	ТекстЗапроса = 
	"ВЫБРАТЬ
	|	СведенияОПользователях.ПоследнийИспользуемыйКлиент КАК ИспользуемыйКлиент,
	|	КОЛИЧЕСТВО(1) КАК Количество
	|ИЗ
	|	РегистрСведений.СведенияОПользователях КАК СведенияОПользователях
	|
	|СГРУППИРОВАТЬ ПО
	|	СведенияОПользователях.ПоследнийИспользуемыйКлиент";
	
	СоответствиеИменМетаданных = Новый Соответствие;
	СоответствиеИменМетаданных.Вставить("Справочник.Пользователи", ТекстЗапроса);
	МодульЦентрМониторинга.ЗаписатьСтатистикуКонфигурации(СоответствиеИменМетаданных);
	
КонецПроцедуры

// См. ВыгрузкаЗагрузкаДанныхПереопределяемый.ПослеЗагрузкиДанных.
Процедура ПослеЗагрузкиДанных(Контейнер) Экспорт
	
	// Сброс решения, принятого администратором в форме "Предупреждение безопасности".
	Если Не ОбщегоНазначения.РазделениеВключено() Тогда
		ПараметрыАдминистрирования = СтандартныеПодсистемыСервер.ПараметрыАдминистрирования();
		
		Если ТипЗнч(ПараметрыАдминистрирования.ПринятоРешениеПоОткрытиюВнешнихОтчетовИОбработок) <> Тип("Булево")
		 Или ПараметрыАдминистрирования.ПринятоРешениеПоОткрытиюВнешнихОтчетовИОбработок Тогда
			
			ПараметрыАдминистрирования.ПринятоРешениеПоОткрытиюВнешнихОтчетовИОбработок = Ложь;
			СтандартныеПодсистемыСервер.УстановитьПараметрыАдминистрирования(ПараметрыАдминистрирования);
		КонецЕсли;
	КонецЕсли;
	
	АвторизоватьТекущегоПользователя();
	
КонецПроцедуры

// См. ОбновлениеИнформационнойБазыБСП.ПриДобавленииОбработчиковОбновления.
Процедура ПриДобавленииОбработчиковОбновления(Обработчики) Экспорт
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = "2.1.3.16"; 
	Обработчик.Процедура = "ПользователиСлужебный.ОбновитьПредопределенныеВидыКонтактнойИнформацииПользователей";
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		Обработчик = Обработчики.Добавить();
		Обработчик.Версия = "2.4.1.1";
		Обработчик.ОбщиеДанные = Истина;
		Обработчик.РежимВыполнения = "Оперативно";
		Обработчик.Процедура = "ПользователиСлужебный.ДобавитьАдминистраторамПравоОткрытияВнешнихОтчетовИОбработок";
	Иначе
		Обработчик = Обработчики.Добавить();
		Обработчик.Версия = "2.4.1.1";
		Обработчик.РежимВыполнения = "Оперативно";
		Обработчик.Процедура = "ПользователиСлужебный.ПереименоватьКлючХраненияРешенияПоОткрытиюВнешнихОтчетовИОбработок";
		Обработчик.ВыполнятьВГруппеОбязательных = Истина;
		Обработчик.Приоритет = 1;
	КонецЕсли;
	
	Обработчик = Обработчики.Добавить();
	Обработчик.НачальноеЗаполнение = Истина;
	Обработчик.РежимВыполнения = "Оперативно";
	Обработчик.Процедура = "ПользователиСлужебный.ЗаполнитьНаименованиеПредопределенныхГруппПользователей";
	
	Если Не ОбщегоНазначения.РазделениеВключено() Тогда
		
		// Пользователи
		Обработчик = Обработчики.Добавить();
		Обработчик.Процедура = "Справочники.Пользователи.ОбработатьДанныеДляПереходаНаНовуюВерсию";
		Обработчик.Версия = "3.1.4.25";
		Обработчик.РежимВыполнения = "Отложенно";
		Обработчик.Идентификатор = Новый УникальныйИдентификатор("d553f38f-196b-4fb7-ac8e-34ffb7025ab5");
		Обработчик.ПроцедураЗаполненияДанныхОбновления = "Справочники.Пользователи.ЗарегистрироватьДанныеКОбработкеДляПереходаНаНовуюВерсию";
		Обработчик.ПроцедураПроверки = "ОбновлениеИнформационнойБазы.ДанныеОбновленыНаНовуюВерсиюПрограммы";
		Обработчик.Комментарий = НСтр("ru = 'Заполнение электронной почты для восстановления паролей из контактной информации пользователей.'");
		Обработчик.ЧитаемыеОбъекты    = "Справочник.Пользователи";
		Обработчик.ИзменяемыеОбъекты  = "Справочник.Пользователи";
		Обработчик.БлокируемыеОбъекты = "Справочник.Пользователи";
		
		Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Обсуждения") Тогда
			Обработчик.ПриоритетыВыполнения = ОбновлениеИнформационнойБазы.ПриоритетыВыполненияОбработчика();
			
			ПриоритетыВыполнения = Обработчик.ПриоритетыВыполнения; // ТаблицаЗначений
			НоваяСтрока = ПриоритетыВыполнения.Добавить();
			НоваяСтрока.Процедура = "ОбсужденияСлужебный.ЗаблокироватьНедействительныхПользователейВСистемеВзаимодействий"; // АПК:277-выкл - выполнен условный вызов
			НоваяСтрока.Порядок = "После";
		КонецЕсли;
		
		// Внешние пользователи
		ТипыВнешнихПользователей = Метаданные.ОпределяемыеТипы.ВнешнийПользователь.Тип.Типы();
		Если ТипыВнешнихПользователей[0] <> Тип("Строка")
			 И ТипыВнешнихПользователей[0] <> Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных")Тогда
			
			Обработчик = Обработчики.Добавить();
			Обработчик.Процедура = "Справочники.ВнешниеПользователи.ОбработатьДанныеДляПереходаНаНовуюВерсию";
			Обработчик.Версия = "3.1.4.25";
			Обработчик.РежимВыполнения = "Отложенно";
			Обработчик.Идентификатор = Новый УникальныйИдентификатор("002f8ac6-dfe6-4d9f-be48-ce3c331aea82");
			Обработчик.ПроцедураЗаполненияДанныхОбновления = "Справочники.ВнешниеПользователи.ЗарегистрироватьДанныеКОбработкеДляПереходаНаНовуюВерсию";
			Обработчик.ПроцедураПроверки = "ОбновлениеИнформационнойБазы.ДанныеОбновленыНаНовуюВерсиюПрограммы";
			Обработчик.Комментарий = НСтр("ru = 'Заполнение электронной почты для восстановления паролей из контактной информации внешних пользователей.'");
			
			Читаемые = Новый Массив;
			Для Каждого ТипВнешнегоПользователя Из ТипыВнешнихПользователей Цикл
				
				Если ТипВнешнегоПользователя = Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных") Тогда
					Продолжить;
				КонецЕсли;
				
				Читаемые.Добавить(Метаданные.НайтиПоТипу(ТипВнешнегоПользователя).ПолноеИмя());
			КонецЦикла;
			Обработчик.ЧитаемыеОбъекты = СтрСоединить(Читаемые, ",");
			
			Обработчик.ИзменяемыеОбъекты  = "Справочник.ВнешниеПользователи";
			Обработчик.БлокируемыеОбъекты = "Справочник.ВнешниеПользователи";
			
			Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Мультиязычность") Тогда
				Обработчик.ПриоритетыВыполнения = ОбновлениеИнформационнойБазы.ПриоритетыВыполненияОбработчика();
				НоваяСтрока = Обработчик.ПриоритетыВыполнения.Добавить();
				НоваяСтрока.Процедура = "МультиязычностьСервер.ОбработатьДанныеДляПереходаНаНовуюВерсию";
				НоваяСтрока.Порядок = "До";
			КонецЕсли;
		КонецЕсли;
		
	КонецЕсли;
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = "3.1.6.8";
	Обработчик.РежимВыполнения = "Оперативно";
	Обработчик.Процедура = "Константы.ИспользоватьГруппыВнешнихПользователей.Обновить";
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = "3.1.8.289";
	Обработчик.РежимВыполнения = "Оперативно";
	Обработчик.Процедура = "РегистрыСведений.СведенияОПользователях.ОбновитьСведенияОПользователяхИОтключитьАутентификацию";
	Обработчик.Комментарий = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = '- Перенос значений устаревших реквизитов ""(не используется) Свойства пользователя ИБ"" из справочников ""Пользователи"" и ""Внешние пользователи"" в регистр сведений ""Сведения о пользователях"".
		           |- Обновление значения реквизита ""Номер картинки состояния"" в регистре сведений ""Сведения о пользователях"".
		           |- Удаление из регистра сведения ""Сведения о пользователях"" записей с несуществующими пользователями и внешними пользователями.
		           |- Сброс лишней аутентификации %1 и аутентификации недействительных пользователей.'"),
		"OpenID-Connect");
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = "3.1.8.198";
	Обработчик.ОбщиеДанные = ОбщегоНазначения.РазделениеВключено();
	Обработчик.НачальноеЗаполнение = Истина;
	Обработчик.РежимВыполнения = "Оперативно";
	Обработчик.Процедура = "ПользователиСлужебный.ПеренестиНастройкиДлиныИСложностиПаролейКонфигуратора";
	Обработчик.Комментарий = НСтр("ru = 'Заполнение и перенос настроек входа пользователей'");
	
КонецПроцедуры

// Смотри также ОбновлениеИнформационнойБазыПереопределяемый.ПриОпределенииНастроек
//
// Параметры:
//  Объекты - Массив из ОбъектМетаданных
//
Процедура ПриОпределенииОбъектовСНачальнымЗаполнением(Объекты) Экспорт
	
	Объекты.Добавить(Метаданные.Справочники.ГруппыВнешнихПользователей);
	Объекты.Добавить(Метаданные.Справочники.ГруппыПользователей);
	
КонецПроцедуры

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииПараметровРаботыКлиента.
Процедура ПриДобавленииПараметровРаботыКлиента(Параметры) Экспорт
	
	// Устарели. Для обратной совместимости. Следует использовать ПользователиКлиент.ЭтоПолноправныйПользователь.
	Параметры.Вставить("ЭтоПолноправныйПользователь", Пользователи.ЭтоПолноправныйПользователь());
	Параметры.Вставить("ЭтоАдминистраторСистемы", Пользователи.ЭтоПолноправныйПользователь(, Истина));
	
КонецПроцедуры

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииИсключенийПоискаСсылок.
Процедура ПриДобавленииИсключенийПоискаСсылок(ИсключенияПоискаСсылок) Экспорт
	
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.СоставыГруппПользователей.ПолноеИмя());
	ИсключенияПоискаСсылок.Добавить(Метаданные.Справочники.ВнешниеПользователи.Реквизиты.ОбъектАвторизации.ПолноеИмя());
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПриОтправкеДанныхПодчиненному.
Процедура ПриОтправкеДанныхПодчиненному(ЭлементДанных, ОтправкаЭлемента, СозданиеНачальногоОбраза, Получатель) Экспорт
	
	ПриОтправкеДанных(ЭлементДанных, ОтправкаЭлемента, Истина);
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПриОтправкеДанныхГлавному.
Процедура ПриОтправкеДанныхГлавному(ЭлементДанных, ОтправкаЭлемента, Получатель) Экспорт
	
	ПриОтправкеДанных(ЭлементДанных, ОтправкаЭлемента, Ложь);
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПриПолученииДанныхОтПодчиненного.
Процедура ПриПолученииДанныхОтПодчиненного(ЭлементДанных, ПолучениеЭлемента, ОтправкаНазад, Отправитель) Экспорт
	
	ПриПолученииДанных(ЭлементДанных, ПолучениеЭлемента, ОтправкаНазад, Истина);
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПриПолученииДанныхОтГлавного.
Процедура ПриПолученииДанныхОтГлавного(ЭлементДанных, ПолучениеЭлемента, ОтправкаНазад, Отправитель) Экспорт
	
	ПриПолученииДанных(ЭлементДанных, ПолучениеЭлемента, ОтправкаНазад, Ложь);
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПослеПолученияДанных.
Процедура ПослеПолученияДанных(Отправитель, Отказ, ПолучениеИзГлавногоУзла) Экспорт
	
	ОбновитьРолиВнешнихПользователей();
	
КонецПроцедуры

// Параметры:
//   ТекущиеДела - см. ТекущиеДелаСервер.ТекущиеДела.
//
Процедура ПриЗаполненииСпискаТекущихДел(ТекущиеДела) Экспорт
	
	// Процедура вызывается только при наличии подсистемы "Текущие дела", поэтому здесь
	// не делается проверка существования подсистемы.
	МодульТекущиеДелаСервер = ОбщегоНазначения.ОбщийМодуль("ТекущиеДелаСервер");
	
	ДобавитьДелоНекорректныеСведенияОПользователях = Не ОбщегоНазначения.РазделениеВключено()
		И Пользователи.ЭтоПолноправныйПользователь(, Истина)
		И Не МодульТекущиеДелаСервер.ДелоОтключено("НекорректныеСведенияОПользователях");
	НекорректныхПользователей = 0;
	Если ДобавитьДелоНекорректныеСведенияОПользователях Тогда
		НекорректныхПользователей = ПользователиДобавленныеСПомощьюКонфигуратора();
	КонецЕсли;
	
	Разделы = МодульТекущиеДелаСервер.РазделыДляОбъекта(Метаданные.Справочники.Пользователи.ПолноеИмя());
	
	Для Каждого Раздел Из Разделы Цикл
		
		Если ДобавитьДелоНекорректныеСведенияОПользователях Тогда
			ИдентификаторПользователи = "НекорректныеСведенияОПользователях" + СтрЗаменить(Раздел.ПолноеИмя(), ".", "");
			Дело = ТекущиеДела.Добавить();
			Дело.Идентификатор  = ИдентификаторПользователи;
			Дело.ЕстьДела       = НекорректныхПользователей > 0;
			Дело.Количество     = НекорректныхПользователей;
			Дело.Представление  = НСтр("ru = 'Некорректные сведения о пользователях'");
			Дело.Форма          = "Справочник.Пользователи.Форма.ПользователиИнформационнойБазы";
			Дело.Владелец       = Раздел;
		КонецЕсли;
		
	КонецЦикла;
	
КонецПроцедуры

// См. УправлениеДоступомПереопределяемый.ПриЗаполненииВидовОграниченийПравОбъектовМетаданных.
Процедура ПриЗаполненииВидовОграниченийПравОбъектовМетаданных(Описание) Экспорт
	
	ДополнениеОписания =
	"
	|Справочник.ВнешниеПользователи.Чтение.ВнешниеПользователи
	|";
	
	Описание = Описание + ДополнениеОписания;
	
КонецПроцедуры

// См. УправлениеСвойствамиПереопределяемый.ПриПолученииПредопределенныхНаборовСвойств
Процедура ПриПолученииПредопределенныхНаборовСвойств(Наборы) Экспорт
	Набор = Наборы.Строки.Добавить();
	Набор.Имя = "Справочник_ВнешниеПользователи";
	Набор.Идентификатор = Новый УникальныйИдентификатор("d9c30d48-a72a-498a-9faa-c078bf652776");
	Набор.Используется  = ПолучитьФункциональнуюОпцию("ИспользоватьВнешнихПользователей");
	
	Набор = Наборы.Строки.Добавить();
	Набор.Имя = "Справочник_Пользователи";
	Набор.Идентификатор = Новый УникальныйИдентификатор("2bf06771-775a-406a-a5dc-45a10e98914f");
КонецПроцедуры

// См. ОбщегоНазначенияПереопределяемый.ПриОпределенииПоддерживаемыхВерсийПрограммныхИнтерфейсов.
// 
// Параметры:
//   СтруктураПоддерживаемыхВерсий - см. ОбщегоНазначенияПереопределяемый.ПриОпределенииПоддерживаемыхВерсийПрограммныхИнтерфейсов.ПоддерживаемыеВерсии
// 
Процедура ПриОпределенииПоддерживаемыхВерсийПрограммныхИнтерфейсов(СтруктураПоддерживаемыхВерсий) Экспорт
	
	МассивВерсий = Новый Массив;
	МассивВерсий.Добавить("1.0.0.1");
	
	СтруктураПоддерживаемыхВерсий.Вставить(
		"НастройкиВходаВМоделиСервиса",
		МассивВерсий);
	
КонецПроцедуры

// Обработчики событий подсистемы СтандартныеПодсистемы.ОбменДанными.

// См. ОбменДаннымиПереопределяемый.ПриНастройкеПодчиненногоУзлаРИБ.
Процедура ПриНастройкеПодчиненногоУзлаРИБ() Экспорт
	
	ОчиститьИдентификаторыНесуществующихПользователейИБ();
	
КонецПроцедуры

// Обработчики событий подсистемы СтандартныеПодсистемы.ВариантыОтчетов.

// См. ВариантыОтчетовПереопределяемый.НастроитьВариантыОтчетов.
Процедура ПриНастройкеВариантовОтчетов(Настройки) Экспорт
	МодульВариантыОтчетов = ОбщегоНазначения.ОбщийМодуль("ВариантыОтчетов");
	МодульВариантыОтчетов.НастроитьОтчетВМодулеМенеджера(Настройки, Метаданные.Отчеты.СведенияОПользователях);
КонецПроцедуры

// Обработчики событий подсистемы СтандартныеПодсистемы.ПодключаемыеКоманды.

// См. СозданиеНаОснованииПереопределяемый.ПриОпределенииОбъектовСКомандамиСозданияНаОсновании.
Процедура ПриОпределенииОбъектовСКомандамиСозданияНаОсновании(Объекты) Экспорт
	
	Объекты.Добавить(Метаданные.Справочники.Пользователи);
	
КонецПроцедуры

// Обработчики событий подсистемы СтандартныеПодсистемы.Пользователи.

// См. процедуру ИзменитьДействияВФорме в общем модуле ПользователиПереопределяемый.
Процедура ПриОпределенииДействийВФорме(Ссылка, ДействияВФорме) Экспорт
	
	ИнтеграцияПодсистемБСП.ПриОпределенииДействийВФорме(Ссылка, ДействияВФорме);
	ПользователиПереопределяемый.ИзменитьДействияВФорме(Ссылка, ДействияВФорме);
	
КонецПроцедуры

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

// Параметры:
//  ИмяПараметра - Строка
//  УстановленныеПараметры - Массив из Строка
//
Процедура УстановкаПараметровСеанса(Знач ИмяПараметра, УстановленныеПараметры) Экспорт
	
	Если ИмяПараметра <> "ТекущийПользователь"
	   И ИмяПараметра <> "ТекущийВнешнийПользователь"
	   И ИмяПараметра <> "АвторизованныйПользователь"
	   И ИмяПараметра <> "ОбновлениеСправочниковПользователей" Тогда
		
		Возврат;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	ПараметрыСеанса.ОбновлениеСправочниковПользователей = Новый ФиксированноеСоответствие(Новый Соответствие);
	Если ИмяПараметра = "ОбновлениеСправочниковПользователей" Тогда
		Возврат;
	КонецЕсли;
	
	Попытка
		Значения = ЗначенияПараметровСеансаТекущийПользователь();
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось установить параметр сеанса %1 по причине:
			           |""%2"".
			           |
			           |Обратитесь к администратору.'"),
			"ТекущийПользователь",
			ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
		ВызватьИсключение ТекстОшибки;
	КонецПопытки;
	
	Если ТипЗнч(Значения) = Тип("Строка") Тогда
		ВызватьИсключение Значения;
	КонецЕсли;
	
	ПараметрыСеанса.ТекущийПользователь        = Значения.ТекущийПользователь;
	ПараметрыСеанса.ТекущийВнешнийПользователь = Значения.ТекущийВнешнийПользователь;
	
	Если ЗначениеЗаполнено(Значения.ТекущийПользователь) Тогда
		ПараметрыСеанса.АвторизованныйПользователь = Значения.ТекущийПользователь;
	Иначе
		ПараметрыСеанса.АвторизованныйПользователь = Значения.ТекущийВнешнийПользователь;
	КонецЕсли;
	
	УстановленныеПараметры.Добавить("ТекущийПользователь");
	УстановленныеПараметры.Добавить("ТекущийВнешнийПользователь");
	УстановленныеПараметры.Добавить("АвторизованныйПользователь");
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Обработчики подписок на события.

// Вызывает обновление представления внешнего пользователя при изменении
// представления его объекта авторизации, а также помечает, как недействительного,
// если объект авторизации помечен на удаление.
//
Процедура ОбновитьВнешнегоПользователяПриЗаписи(Знач Объект, Отказ) Экспорт
	
	Если Объект.ОбменДанными.Загрузка Тогда
		Возврат;
	КонецЕсли;
	
	Если СтандартныеПодсистемыСервер.ЭтоИдентификаторОбъектаМетаданных(Объект) Тогда
		Возврат;
	КонецЕсли;
	
	ОбновитьВнешнегоПользователя(Объект.Ссылка, Объект.ПометкаУдаления);
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции работы с настройками входа пользователей.

// Возвращаемое значение:
//  Структура:
//   * Общие               - см. Пользователи.НовоеОписаниеОбщихНастроекВхода
//   * Пользователи        - см. Пользователи.НовоеОписаниеНастроекВхода
//   * ВнешниеПользователи - см. Пользователи.НовоеОписаниеНастроекВхода
//
Функция НастройкиВхода() Экспорт
	
	Настройки = Новый Структура;
	Настройки.Вставить("Общие",               Пользователи.НовоеОписаниеОбщихНастроекВхода());
	Настройки.Вставить("Пользователи",        Пользователи.НовоеОписаниеНастроекВхода());
	Настройки.Вставить("ВнешниеПользователи", Пользователи.НовоеОписаниеНастроекВхода());
	
	РазделениеВключено = ОбщегоНазначения.РазделениеВключено();
	ОбщиеНастройкиВходаИспользуются = Пользователи.ОбщиеНастройкиВходаИспользуются();
	ОкруглятьПолитикуПаролей = Не РазделениеВключено И ОбщиеНастройкиВходаИспользуются;
	
	УстановитьПривилегированныйРежим(Истина);
	
	СохраненныеНастройки = Константы.НастройкиВходаПользователей.Получить().Получить();
	
	Если ТипЗнч(СохраненныеНастройки) = Тип("Структура") Тогда
		
		Для Каждого Настройка Из Настройки Цикл
			
			Если Не СохраненныеНастройки.Свойство(Настройка.Ключ)
			 Или ТипЗнч(СохраненныеНастройки[Настройка.Ключ]) <> Тип("Структура") Тогда
				Продолжить;
			КонецЕсли;
			
			НачальныеНастройки = Настройка.Значение;
			ТекущиеНастройки = СохраненныеНастройки[Настройка.Ключ];
			
			Для Каждого НачальнаяНастройка Из НачальныеНастройки Цикл
				
				Если Не ТекущиеНастройки.Свойство(НачальнаяНастройка.Ключ)
				 Или ТипЗнч(ТекущиеНастройки[НачальнаяНастройка.Ключ]) <> ТипЗнч(НачальнаяНастройка.Значение) Тогда
					Продолжить;
				КонецЕсли;
				НачальныеНастройки[НачальнаяНастройка.Ключ] = ТекущиеНастройки[НачальнаяНастройка.Ключ];
			КонецЦикла;
		КонецЦикла;
		
	КонецЕсли;
	
	Если РазделениеВключено
	 Или ВнешниеПользователи.ИспользоватьВнешнихПользователей() Тогда
		
		Настройки.Общие.ПоказыватьВСпискеВыбора = "СкрытоИВыключеноДляВсехПользователей";
		
	ИначеЕсли Настройки.Общие.ПоказыватьВСпискеВыбора <> "ВключеноДляНовыхПользователей"
	        И Настройки.Общие.ПоказыватьВСпискеВыбора <> "ВыключеноДляНовыхПользователей"
	        И Настройки.Общие.ПоказыватьВСпискеВыбора <> "СкрытоИВключеноДляВсехПользователей"
	        И Настройки.Общие.ПоказыватьВСпискеВыбора <> "СкрытоИВыключеноДляВсехПользователей" Тогда
		
		Настройки.Общие.ПоказыватьВСпискеВыбора =
			Пользователи.НовоеОписаниеОбщихНастроекВхода().ПоказыватьВСпискеВыбора;
	КонецЕсли;
	
	// Заполнение общих настроек.
	ЗаполнитьОбщиеНастройкиИзОбщейПолитикиПаролей(Настройки.Общие);
	Если ОкруглятьПолитикуПаролей Тогда
		ОбновитьОбщуюПолитикуПаролей(Настройки.Общие);
	КонецЕсли;
	
	// Заполнение настроек обычных пользователей.
	ЗаполнитьНастройкиИзПолитикиПаролейПользователей(Настройки.Пользователи);
	Если ОкруглятьПолитикуПаролей Тогда
		ОбновитьПолитикуПаролейПользователей(Настройки.Пользователи);
	КонецЕсли;
	
	// Заполнение настроек внешних пользователей.
	Если Настройки.Общие.ОтдельныеНастройкиДляВнешнихПользователей Тогда
		ЗаполнитьНастройкиИзПолитикиПаролейВнешнихПользователей(Настройки.ВнешниеПользователи);
	КонецЕсли;
	
	Если Не РазделениеВключено Тогда
		Если Настройки.Общие.ОтдельныеНастройкиДляВнешнихПользователей
		   И ОбщиеНастройкиВходаИспользуются Тогда
			ОбновитьПолитикуПаролейВнешнихПользователей(Настройки.ВнешниеПользователи);
		Иначе
			ОбновитьПолитикуПаролейВнешнихПользователей(Неопределено);
		КонецЕсли;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Ложь);
	
	Возврат Настройки;
	
КонецФункции

// Параметры:
//  Настройки - см. Пользователи.НовоеОписаниеОбщихНастроекВхода
//
Процедура ЗаполнитьОбщиеНастройкиИзОбщейПолитикиПаролей(Настройки)
	
	НастройкиБлокировки = БлокировкаАутентификации.ПолучитьНастройки();
	
	Настройки.ДлительностьБлокировкиВводаПароля =
		Окр(НастройкиБлокировки.ДлительностьБлокировки / 60);
	
	Настройки.КоличествоПопытокВводаПароляДоБлокировки =
		НастройкиБлокировки.МаксимальноеКоличествоНеуспешныхПопыток;
	
КонецПроцедуры

// Параметры:
//  Настройки - см. Пользователи.НовоеОписаниеОбщихНастроекВхода
//
Процедура ОбновитьОбщуюПолитикуПаролей(Настройки) Экспорт
	
	НастройкиБлокировки = БлокировкаАутентификации.ПолучитьНастройки();
	Записать = Ложь;
	
	Если НастройкиБлокировки.ДлительностьБлокировки
	  <> Настройки.ДлительностьБлокировкиВводаПароля * 60 Тогда
		
		Записать = Истина;
		НастройкиБлокировки.ДлительностьБлокировки =
			Настройки.ДлительностьБлокировкиВводаПароля * 60;
	КонецЕсли;
	
	Если НастройкиБлокировки.МаксимальноеКоличествоНеуспешныхПопыток
	  <> Настройки.КоличествоПопытокВводаПароляДоБлокировки Тогда
	
		Записать = Истина;
		НастройкиБлокировки.МаксимальноеКоличествоНеуспешныхПопыток =
			Настройки.КоличествоПопытокВводаПароляДоБлокировки;
	КонецЕсли;
	
	Если Записать Тогда
		БлокировкаАутентификации.УстановитьНастройки(НастройкиБлокировки);
	КонецЕсли;
	
КонецПроцедуры

// Параметры:
//  Настройки - см. Пользователи.НовоеОписаниеНастроекВхода
//
Процедура ЗаполнитьНастройкиИзПолитикиПаролейПользователей(Настройки)
	
	СекундВДне = 24*60*60;
	РазделениеВключено = ОбщегоНазначения.РазделениеВключено();
	
	Настройки.ПарольДолженОтвечатьТребованиямСложности =
		ПолучитьПроверкуСложностиПаролейПользователей();
	
	Настройки.МинимальнаяДлинаПароля =
		ПолучитьМинимальнуюДлинуПаролейПользователей();
	
	Если Не РазделениеВключено Тогда
		Настройки.МаксимальныйСрокДействияПароля =
			Окр(ПолучитьМаксимальныйСрокДействияПаролейПользователей() / СекундВДне);
	КонецЕсли;
	
	Настройки.МинимальныйСрокДействияПароля =
		Окр(ПолучитьМинимальныйСрокДействияПаролейПользователей() / СекундВДне);
	
	Настройки.ЗапретитьПовторениеПароляСредиПоследних =
		ПолучитьОграничениеПовторенияПаролейПользователейСредиПоследних();
	
	Если Не РазделениеВключено Тогда
		Настройки.ПредупреждатьОбОкончанииСрокаДействияПароля =
			Окр(ПолучитьСрокПредупрежденияОбИстеченииСрокаДействияПаролейПользователей() / СекундВДне);
	КонецЕсли;
	
КонецПроцедуры

// Параметры:
//  Настройки - см. Пользователи.НовоеОписаниеНастроекВхода
//
Процедура ОбновитьПолитикуПаролейПользователей(Настройки) Экспорт
	
	СекундВДне = 24*60*60;
	РазделениеВключено = ОбщегоНазначения.РазделениеВключено();
	
	Если ПолучитьПроверкуСложностиПаролейПользователей()
	  <> Настройки.ПарольДолженОтвечатьТребованиямСложности Тогда
		
		УстановитьПроверкуСложностиПаролейПользователей(
			Настройки.ПарольДолженОтвечатьТребованиямСложности);
	 КонецЕсли;
	
	Если ПолучитьМинимальнуюДлинуПаролейПользователей()
	  <> Настройки.МинимальнаяДлинаПароля Тогда
	
		УстановитьМинимальнуюДлинуПаролейПользователей(
			Настройки.МинимальнаяДлинаПароля);
	КонецЕсли;
	
	МаксимальныйСрокДействияПароля = ?(РазделениеВключено,
		0, Настройки.МаксимальныйСрокДействияПароля);
	
	Если ПолучитьМаксимальныйСрокДействияПаролейПользователей()
	  <> МаксимальныйСрокДействияПароля * СекундВДне Тогда
	
		УстановитьМаксимальныйСрокДействияПаролейПользователей(
			МаксимальныйСрокДействияПароля * СекундВДне);
	КонецЕсли;
	
	Если ПолучитьМинимальныйСрокДействияПаролейПользователей()
	  <> Настройки.МинимальныйСрокДействияПароля * СекундВДне Тогда
	
		УстановитьМинимальныйСрокДействияПаролейПользователей(
			Настройки.МинимальныйСрокДействияПароля * СекундВДне);
	КонецЕсли;
	
	Если ПолучитьОграничениеПовторенияПаролейПользователейСредиПоследних()
	  <> Настройки.ЗапретитьПовторениеПароляСредиПоследних Тогда
	
		УстановитьОграничениеПовторенияПаролейПользователейСредиПоследних(
			Настройки.ЗапретитьПовторениеПароляСредиПоследних);
	КонецЕсли;
	
	ПредупреждатьОбОкончанииСрокаДействияПароля = ?(РазделениеВключено,
		0, Настройки.ПредупреждатьОбОкончанииСрокаДействияПароля);
	
	Если ПолучитьСрокПредупрежденияОбИстеченииСрокаДействияПаролейПользователей()
	  <> ПредупреждатьОбОкончанииСрокаДействияПароля * СекундВДне Тогда
	
		УстановитьСрокПредупрежденияОбИстеченииСрокаДействияПаролейПользователей(
			ПредупреждатьОбОкончанииСрокаДействияПароля * СекундВДне);
	КонецЕсли;
	
КонецПроцедуры

// Параметры:
//  Настройки - см. Пользователи.НовоеОписаниеНастроекВхода
//
Процедура ЗаполнитьНастройкиИзПолитикиПаролейВнешнихПользователей(Настройки)
	
	ПолитикаПаролей = ПолитикиПаролейПользователей.НайтиПоИмени(
		ИмяПолитикиПаролейВнешнихПользователей());
		
	Если ПолитикаПаролей = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	СекундВДне = 24*60*60;
	РазделениеВключено = ОбщегоНазначения.РазделениеВключено();
	
	Настройки.ПарольДолженОтвечатьТребованиямСложности =
		ПолитикаПаролей.ПроверкаСложностиПаролей;
	
	Настройки.МинимальнаяДлинаПароля =
		ПолитикаПаролей.МинимальнаяДлинаПаролей;
	
	Если Не РазделениеВключено Тогда
		Настройки.МаксимальныйСрокДействияПароля =
			Окр(ПолитикаПаролей.МаксимальныйСрокДействияПаролей / СекундВДне);
	КонецЕсли;
	
	Настройки.МинимальныйСрокДействияПароля =
		Окр(ПолитикаПаролей.МинимальныйСрокДействияПаролей / СекундВДне);
	
	Настройки.ЗапретитьПовторениеПароляСредиПоследних =
		ПолитикаПаролей.ОграничениеПовторенияПаролейСредиПоследних;
	
	Если Не РазделениеВключено Тогда
		Настройки.ПредупреждатьОбОкончанииСрокаДействияПароля =
			Окр(ПолитикаПаролей.СрокПредупрежденияОбИстеченииСрокаДействияПаролей / СекундВДне);
	КонецЕсли;
	
КонецПроцедуры

// Параметры:
//  Настройки - см. Пользователи.НовоеОписаниеНастроекВхода
//
Процедура ОбновитьПолитикуПаролейВнешнихПользователей(Настройки) Экспорт
	
	ИмяПолитики = ИмяПолитикиПаролейВнешнихПользователей();
	
	ПолитикаПаролей = ПолитикиПаролейПользователей.НайтиПоИмени(ИмяПолитики);
	Если Настройки = Неопределено Тогда
		Если ПолитикаПаролей <> Неопределено Тогда
			ПолитикаПаролей.Удалить();
		КонецЕсли;
		Возврат;
	КонецЕсли;
	
	Записать = Ложь;
	Если ПолитикаПаролей = Неопределено Тогда
		ПолитикаПаролей = ПолитикиПаролейПользователей.СоздатьПолитику();
		ПолитикаПаролей.Имя = ИмяПолитики;
		Записать = Истина;
	КонецЕсли;
	
	СекундВДне = 24*60*60;
	РазделениеВключено = ОбщегоНазначения.РазделениеВключено();
	
	ОбновитьСвойствоПолитики(ПолитикаПаролей.ПроверкаСложностиПаролей,
		Настройки.ПарольДолженОтвечатьТребованиямСложности, Записать);
	
	ОбновитьСвойствоПолитики(ПолитикаПаролей.МинимальнаяДлинаПаролей,
		Настройки.МинимальнаяДлинаПароля, Записать);
	
	ОбновитьСвойствоПолитики(ПолитикаПаролей.МаксимальныйСрокДействияПаролей,
		?(РазделениеВключено, 0, Настройки.МаксимальныйСрокДействияПароля * СекундВДне), Записать);
	
	ОбновитьСвойствоПолитики(ПолитикаПаролей.МинимальныйСрокДействияПаролей,
		Настройки.МинимальныйСрокДействияПароля * СекундВДне, Записать);
	
	ОбновитьСвойствоПолитики(ПолитикаПаролей.ОграничениеПовторенияПаролейСредиПоследних,
		Настройки.ЗапретитьПовторениеПароляСредиПоследних, Записать);
	
	ОбновитьСвойствоПолитики(ПолитикаПаролей.СрокПредупрежденияОбИстеченииСрокаДействияПаролей,
		?(РазделениеВключено, 0, Настройки.ПредупреждатьОбОкончанииСрокаДействияПароля * СекундВДне), Записать);
	
	Если Записать Тогда
		ПолитикаПаролей.Записать();
	КонецЕсли;
	
КонецПроцедуры

Процедура ОбновитьСвойствоПолитики(ЗначениеПолитики, ЗначениеНастройки, Записать)
	
	Если ЗначениеПолитики = ЗначениеНастройки Тогда
		Возврат;
	КонецЕсли;
	
	ЗначениеПолитики = ЗначениеНастройки;
	Записать = Истина;
	
КонецПроцедуры

// Параметры:
//  ПользовательИБ - ПользовательИнформационнойБазы
//  ЭтоВнешнийПользователь - Булево
//
Процедура УстановитьПолитикуПаролей(ПользовательИБ, ЭтоВнешнийПользователь) Экспорт
	
	ИмяПолитикиПаролей = ИмяПолитикиПаролей(ЭтоВнешнийПользователь);
	Если ПользовательИБ.ИмяПолитикиПаролей <> ИмяПолитикиПаролей Тогда
		ПользовательИБ.ИмяПолитикиПаролей = ИмяПолитикиПаролей;
	КонецЕсли;
	
КонецПроцедуры

// Параметры:
//  ЭтоВнешнийПользователь - Булево
//
Функция ИмяПолитикиПаролей(ЭтоВнешнийПользователь) Экспорт
	
	Если ЭтоВнешнийПользователь Тогда
		Возврат ИмяПолитикиПаролейВнешнихПользователей();
	КонецЕсли;
	
	Возврат "";
	
КонецФункции

Функция ИмяПолитикиПаролейВнешнихПользователей()
	
	Возврат "ExternalUsers";
	
КонецФункции

// У всех пользователей ИБ устанавливает реквизит ПоказыватьВСпискеВыбора.
// Параметры:
//  Показывать - Булево
//
Процедура УстановитьРеквизитПоказыватьВСпискеВыбораУВсехПользователейИБ(Показывать) Экспорт
	
	Скрытые = Новый Соответствие;
	
	Если Показывать Тогда
		Запрос = Новый Запрос;
		Запрос.УстановитьПараметр("ПустойУникальныйИдентификатор",
			ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
		Запрос.Текст =
		"ВЫБРАТЬ
		|	Пользователи.ИдентификаторПользователяИБ КАК ИдентификаторПользователяИБ
		|ИЗ
		|	Справочник.Пользователи КАК Пользователи
		|ГДЕ
		|	Пользователи.ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор
		|	И (Пользователи.Служебный
		|			ИЛИ Пользователи.Недействителен)";
		Выборка = Запрос.Выполнить().Выбрать();
		Скрытые.Вставить(Выборка.ИдентификаторПользователяИБ, Истина);
	КонецЕсли;
	
	ПользователиИБ = ПользователиИнформационнойБазы.ПолучитьПользователей();
	Для Каждого ПользовательИБ Из ПользователиИБ Цикл
		Если Не Показывать
		 Или Скрытые.Получить(ПользовательИБ.УникальныйИдентификатор) <> Неопределено Тогда
			ПоказыватьВСпискеВыбора = Ложь;
		Иначе
			ПоказыватьВСпискеВыбора = ПользовательИБ.АутентификацияСтандартная;
		КонецЕсли;
		Если ПользовательИБ.ПоказыватьВСпискеВыбора <> ПоказыватьВСпискеВыбора Тогда
			ПользовательИБ.ПоказыватьВСпискеВыбора = ПоказыватьВСпискеВыбора;
			ПользовательИБ.Записать();
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции работы с паролем.

// Создает новый пароль, соответствующий заданным правилам проверки сложности.
// Для облегчения запоминания, пароль формируется из слогов (согласная-гласная).
//
// Параметры:
//  ПараметрыПароля - Структура - возвращается функций ПараметрыПароля.
//  ГСЧ             - ГенераторСлучайныхЧисел - если уже используется.
//                  - Неопределено - создать новый.
//
// Возвращаемое значение:
//  Строка - новый пароль.
//
Функция СоздатьПароль(ПараметрыПароля, ГСЧ = Неопределено) Экспорт
	
	НовыйПароль = "";
	
	СогласныеСтрочные               = ПараметрыПароля.СогласныеБуквыСтрочные;
	СогласныеЗаглавные              = ПараметрыПароля.СогласныеБуквыЗаглавные;
	КоличествоСогласныхСтрочных     = СтрДлина(СогласныеСтрочные);
	КоличествоСогласныхЗаглавных    = СтрДлина(СогласныеЗаглавные);
	ИспользоватьСогласные           = (КоличествоСогласныхСтрочных > 0)
	                                  ИЛИ (КоличествоСогласныхЗаглавных > 0);
	
	ГласныеСтрочные                 = ПараметрыПароля.ГласныеБуквыСтрочные;
	ГласныеЗаглавные                = ПараметрыПароля.ГласныеБуквыЗаглавные;
	КоличествоГласныхСтрочных       = СтрДлина(ГласныеСтрочные);
	КоличествоГласныхЗаглавных      = СтрДлина(ГласныеЗаглавные);
	ИспользоватьГласные             = (КоличествоГласныхСтрочных > 0) 
	                                  ИЛИ (КоличествоГласныхЗаглавных > 0);
	
	Цифры                   = ПараметрыПароля.Цифры;
	КоличествоЦифр          = СтрДлина(Цифры);
	ИспользоватьЦифры       = (КоличествоЦифр > 0);
	
	СпецСимволы             = ПараметрыПароля.СпецСимволы;
	КоличествоСпецСимволов  = СтрДлина(СпецСимволы);
	ИспользоватьСпецСимволы = (КоличествоСпецСимволов > 0);
	
	// Создание генератора случайных чисел.
	Если ГСЧ = Неопределено Тогда
		ГСЧ = Пользователи.СвойстваПароля().ГСЧ;
	КонецЕсли;
	
	Счетчик = 0;
	
	МаксимальнаяДлина           = ПараметрыПароля.МаксимальнаяДлина;
	МинимальнаяДлина            = ПараметрыПароля.МинимальнаяДлина;
	
	// Определение позиции спец. символов и цифр.
	Если ПараметрыПароля.ПроверятьУсловияСложности Тогда
		УстановитьСтрочную      = ПараметрыПароля.СтрочныеБуквыПроверятьНаличие;
		УстановитьЗаглавную     = ПараметрыПароля.ЗаглавныеБуквыПроверятьНаличие;
		УстановитьЦифру         = ПараметрыПароля.ЦифрыПроверятьНаличие;
		УстановитьСпецСимвол    = ПараметрыПароля.СпецСимволыПроверятьНаличие;
	Иначе
		УстановитьСтрочную      = (КоличествоГласныхСтрочных > 0) 
		                          ИЛИ (КоличествоСогласныхСтрочных > 0);
		УстановитьЗаглавную     = (КоличествоГласныхЗаглавных > 0) 
		                          ИЛИ (КоличествоСогласныхЗаглавных > 0);
		УстановитьЦифру         = ИспользоватьЦифры;
		УстановитьСпецСимвол    = ИспользоватьСпецСимволы;
	КонецЕсли;
	
	Пока Счетчик < МаксимальнаяДлина Цикл
		
		// Начало с согласной буквы.
		Если ИспользоватьСогласные Тогда
			Если УстановитьЗаглавную И УстановитьСтрочную Тогда
				СтрокаПоиска = СогласныеСтрочные + СогласныеЗаглавные;
				ВерхняяГраница = КоличествоСогласныхСтрочных + КоличествоСогласныхЗаглавных;
			ИначеЕсли УстановитьЗаглавную Тогда
				СтрокаПоиска = СогласныеЗаглавные;
				ВерхняяГраница = КоличествоСогласныхЗаглавных;
			Иначе
				СтрокаПоиска = СогласныеСтрочные;
				ВерхняяГраница = КоличествоСогласныхСтрочных;
			КонецЕсли;
			Если ПустаяСтрока(СтрокаПоиска) Тогда
				СтрокаПоиска = СогласныеСтрочные + СогласныеЗаглавные;
				ВерхняяГраница = КоличествоСогласныхСтрочных + КоличествоСогласныхЗаглавных;
			КонецЕсли;
			Символ = Сред(СтрокаПоиска, ГСЧ.СлучайноеЧисло(1, ВерхняяГраница), 1);
			Если Символ = ВРЕГ(Символ) Тогда
				Если УстановитьЗаглавную Тогда
					УстановитьЗаглавную = (ГСЧ.СлучайноеЧисло(0, 1) = 1);
				КонецЕсли;
			Иначе
				УстановитьСтрочную = Ложь;
			КонецЕсли;
			НовыйПароль = НовыйПароль + Символ;
			Счетчик     = Счетчик + 1;
			Если Счетчик >= МинимальнаяДлина Тогда
				Прервать;
			КонецЕсли;
		КонецЕсли;
		
		// Добавление гласных.
		Если ИспользоватьГласные Тогда
			Если УстановитьЗаглавную И УстановитьСтрочную Тогда
				СтрокаПоиска = ГласныеСтрочные + ГласныеЗаглавные;
				ВерхняяГраница = КоличествоГласныхСтрочных + КоличествоГласныхЗаглавных;
			ИначеЕсли УстановитьЗаглавную Тогда
				СтрокаПоиска = ГласныеЗаглавные;
				ВерхняяГраница = КоличествоГласныхЗаглавных;
			Иначе
				СтрокаПоиска = ГласныеСтрочные;
				ВерхняяГраница = КоличествоГласныхСтрочных;
			КонецЕсли;
			Если ПустаяСтрока(СтрокаПоиска) Тогда
				СтрокаПоиска = ГласныеСтрочные + ГласныеЗаглавные;
				ВерхняяГраница = КоличествоГласныхСтрочных + КоличествоГласныхЗаглавных;
			КонецЕсли;
			Символ = Сред(СтрокаПоиска, ГСЧ.СлучайноеЧисло(1, ВерхняяГраница), 1);
			Если Символ = ВРЕГ(Символ) Тогда
				УстановитьЗаглавную = Ложь;
			Иначе
				УстановитьСтрочную = Ложь;
			КонецЕсли;
			НовыйПароль = НовыйПароль + Символ;
			Счетчик     = Счетчик + 1;
			Если Счетчик >= МинимальнаяДлина Тогда
				Прервать;
			КонецЕсли;
		КонецЕсли;
	
		// Добавление цифр.
		Если ИспользоватьЦифры И УстановитьЦифру Тогда
			УстановитьЦифру = (ГСЧ.СлучайноеЧисло(0, 1) = 1);
			Символ          = Сред(Цифры, ГСЧ.СлучайноеЧисло(1, КоличествоЦифр), 1);
			НовыйПароль     = НовыйПароль + Символ;
			Счетчик         = Счетчик + 1;
			Если Счетчик >= МинимальнаяДлина Тогда
				Прервать;
			КонецЕсли;
		КонецЕсли;
		
		// Добавление спец. символов.
		Если ИспользоватьСпецСимволы И УстановитьСпецСимвол Тогда
			УстановитьСпецСимвол = (ГСЧ.СлучайноеЧисло(0, 1) = 1);
			Символ      = Сред(СпецСимволы, ГСЧ.СлучайноеЧисло(1, КоличествоСпецСимволов), 1);
			НовыйПароль = НовыйПароль + Символ;
			Счетчик     = Счетчик + 1;
			Если Счетчик >= МинимальнаяДлина Тогда
				Прервать;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
	Возврат НовыйПароль;
	
КонецФункции

// Возвращает стандартные параметры с учетом длины и сложности.
//
// Параметры:
//  НаименьшаяДлина - Число - наименьшая длина пароля (по умолчанию - 7).
//  Сложный         - Булево - учитывать требования проверки сложности пароля.
//
// Возвращаемое значение:
//  Структура - параметры создания пароля.
//
Функция ПараметрыПароля(НаименьшаяДлина = 7, Сложный = Ложь) Экспорт
	
	ПараметрыПароля = Новый Структура();
	ПараметрыПароля.Вставить("МинимальнаяДлина",                НаименьшаяДлина);
	ПараметрыПароля.Вставить("МаксимальнаяДлина",               99);
	ПараметрыПароля.Вставить("ГласныеБуквыСтрочные",            "aeiouy"); 
	ПараметрыПароля.Вставить("ГласныеБуквыЗаглавные",           "AEIOUY");
	ПараметрыПароля.Вставить("СогласныеБуквыСтрочные",          "bcdfghjklmnpqrstvwxz");
	ПараметрыПароля.Вставить("СогласныеБуквыЗаглавные",         "BCDFGHJKLMNPQRSTVWXZ");
	ПараметрыПароля.Вставить("Цифры",                           "0123456789");
	ПараметрыПароля.Вставить("СпецСимволы",                     " _.,!?");
	ПараметрыПароля.Вставить("ПроверятьУсловияСложности",       Сложный);
	ПараметрыПароля.Вставить("ЗаглавныеБуквыПроверятьНаличие",  Истина);
	ПараметрыПароля.Вставить("СтрочныеБуквыПроверятьНаличие",   Истина);
	ПараметрыПароля.Вставить("ЦифрыПроверятьНаличие",           Истина);
	ПараметрыПароля.Вставить("СпецСимволыПроверятьНаличие",     Ложь);
	
	Возврат ПараметрыПароля;
	
КонецФункции

// Проверяет наличие учетной записи и прав, необходимых для изменения пароля.
//
// Параметры:
//  Пользователь - СправочникСсылка.Пользователи
//               - СправочникСсылка.ВнешниеПользователи - пользователь для смены пароля.
//
//  ДополнительныеПараметры - Структура - возвращаемое значение со свойствами:
//   * ТекстОшибки                 - Строка - описание ошибки, если нет возможности изменить пароль.
//   * ИдентификаторПользователяИБ - УникальныйИдентификатор - идентификатор пользователя ИБ.
//   * ЭтоТекущийПользовательИБ    - Булево - Истина, если пользователь является текущим.
//
// Возвращаемое значение:
//  Булево - Ложь, если нет возможности изменить пароль.
//
Функция ВозможноИзменитьПароль(Пользователь, ДополнительныеПараметры = Неопределено) Экспорт
	
	Если ТипЗнч(ДополнительныеПараметры) <> Тип("Структура") Тогда
		ДополнительныеПараметры = Новый Структура;
	КонецЕсли;
	
	Если Не ДополнительныеПараметры.Свойство("ЭтоСлужебныйПользователь")
	   И ОбщегоНазначения.РазделениеВключено()
	   И ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ПользователиВМоделиСервиса")
	   И Пользователь <> Пользователи.АвторизованныйПользователь() Тогда
		
		МодульПользователиСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ПользователиСлужебныйВМоделиСервиса");
		ДействияСПользователемСервиса = МодульПользователиСлужебныйВМоделиСервиса.ПолучитьДействияСПользователемСервиса(
			Пользователь);
		
		Если Не ДействияСПользователемСервиса.ИзменениеПароля Тогда
			ДополнительныеПараметры.Вставить("ТекстОшибки", СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Сервис: Недостаточно прав для изменения пароля пользователя ""%1"".'"), Пользователь));
			Возврат Ложь;
		КонецЕсли;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	РеквизитыПользователя = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
		Пользователь, "Ссылка, Недействителен, ИдентификаторПользователяИБ, Подготовлен");
	
	Если РеквизитыПользователя.Ссылка <> Пользователь Тогда
		РеквизитыПользователя.Ссылка = ОбщегоНазначения.МенеджерОбъектаПоСсылке(Пользователь).ПустаяСсылка();
		РеквизитыПользователя.Недействителен = Ложь;
		РеквизитыПользователя.Подготовлен = Ложь;
		РеквизитыПользователя.ИдентификаторПользователяИБ = ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор();
	КонецЕсли;
	
	Если ДополнительныеПараметры.Свойство("ПроверятьДействительностьПользователя")
	   И РеквизитыПользователя.Недействителен <> Ложь Тогда
		
		ДополнительныеПараметры.Вставить("ТекстОшибки", СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Пользователь ""%1"" недействителен.'"), Пользователь));
		Возврат Ложь;
	КонецЕсли;
	
	ИдентификаторПользователяИБ = РеквизитыПользователя.ИдентификаторПользователяИБ;
	ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(ИдентификаторПользователяИБ);
	
	УстановитьПривилегированныйРежим(Ложь);
	
	Если ДополнительныеПараметры.Свойство("ПроверятьНаличиеПользователяИБ")
	   И ПользовательИБ = Неопределено Тогда
		
		ДополнительныеПараметры.Вставить("ТекстОшибки", СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не существует учетная запись пользователя ""%1"".'"), Пользователь));
		Возврат Ложь;
	КонецЕсли;
	
	ДополнительныеПараметры.Вставить("ИдентификаторПользователяИБ", ИдентификаторПользователяИБ);
	ДополнительныеПараметры.Вставить("ИмяДляВхода", ?(ПользовательИБ = Неопределено, "", ПользовательИБ.Имя));
	
	ИдентификаторТекущегоПользователяИБ = ПользователиИнформационнойБазы.ТекущийПользователь().УникальныйИдентификатор;
	ДополнительныеПараметры.Вставить("ЭтоТекущийПользовательИБ", ИдентификаторПользователяИБ = ИдентификаторТекущегоПользователяИБ);
	
	УровеньДоступа = УровеньДоступаКСвойствамПользователя(РеквизитыПользователя);
	
	Если Не ДополнительныеПараметры.ЭтоТекущийПользовательИБ
	   И Не УровеньДоступа.НастройкиДляВхода Тогда
		
		ДополнительныеПараметры.Вставить("ТекстОшибки", СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Недостаточно прав для изменения пароля пользователя ""%1"".'"), Пользователь));
		Возврат Ложь;
	КонецЕсли;
	
	ДополнительныеПараметры.Вставить("ПарольУстановлен",
		ПользовательИБ <> Неопределено И ПользовательИБ.ПарольУстановлен);
	
	Если ПользовательИБ <> Неопределено И ПользовательИБ.ЗапрещеноИзменятьПароль Тогда
		Если УровеньДоступа.НастройкиДляВхода Тогда
			Если ДополнительныеПараметры.Свойство("УчитыватьСвойствоЗапрещеноИзменятьПароль") Тогда
				ДополнительныеПараметры.Вставить("ТекстОшибки", СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Установлен запрет изменения пароля пользователя ""%1"".'"), Пользователь));
				Возврат Ложь;
			КонецЕсли;
		Иначе
			ДополнительныеПараметры.Вставить("ТекстОшибки", СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Установлен запрет изменения пароля пользователя ""%1"".
				           |Обратитесь к администратору.'"), Пользователь));
			Возврат Ложь;
		КонецЕсли;
	КонецЕсли;
	
	Если ДополнительныеПараметры.Свойство("УчитыватьСвойствоАутентификацияСтандартная")
	   И ПользовательИБ <> Неопределено
	   И Не ПользовательИБ.АутентификацияСтандартная Тогда
		Возврат Ложь;
	КонецЕсли;
	
	// Проверка минимального срока действия пароля.
	Если ПользовательИБ = Неопределено
	 Или Не ОбщегоНазначения.РазделениеВключено()
	   И УровеньДоступа.НастройкиДляВхода Тогда
		
		Возврат Истина;
	КонецЕсли;
	
	ИмяПолитикиПаролей = ИмяПолитикиПаролей(
		ТипЗнч(Пользователь) = Тип("СправочникСсылка.ВнешниеПользователи"));
	
	УстановитьПривилегированныйРежим(Истина);
	ПолитикаПаролей = ПолитикиПаролейПользователей.НайтиПоИмени(ИмяПолитикиПаролей);
	Если ПолитикаПаролей = Неопределено Тогда
		МинимальныйСрокДействияПаролей = ПолучитьМинимальныйСрокДействияПаролейПользователей();
	Иначе
		МинимальныйСрокДействияПаролей = ПолитикаПаролей.МинимальныйСрокДействияПаролей;
	КонецЕсли;
	УстановитьПривилегированныйРежим(Ложь);
	
	Если Не ЗначениеЗаполнено(МинимальныйСрокДействияПаролей) Тогда
		Возврат Истина;
	КонецЕсли;
	
	ОстатокМинимальногоСрокаДействияПароля = МинимальныйСрокДействияПаролей
		- (ТекущаяУниверсальнаяДата() - ПользовательИБ.ДатаУстановкиПароля);
	
	Если ОстатокМинимальногоСрокаДействияПароля <= 0 Тогда
		Возврат Истина;
	КонецЕсли;
	
	КоличествоДней = Окр(ОстатокМинимальногоСрокаДействияПароля / (24*60*60));
	Если КоличествоДней = 0 Тогда
		КоличествоДней = 1;
	КонецЕсли;
	
	ЧислоИПредмет = Формат(КоличествоДней, "ЧГ=") + " "
		+ ПользователиСлужебныйКлиентСервер.ПредметЦелогоЧисла(КоличествоДней,
			"", НСтр("ru = 'день,дня,дней,,,,,,0'"));
	
	ДополнительныеПараметры.Вставить("ТекстОшибки", СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Пароль можно будет сменить только через %1.'"), ЧислоИПредмет));
	
	Возврат Ложь;
	
КонецФункции

// Для формы СменаПароля и перед записью пользователя ИБ.
// Выполняет проверку нового пароля, старого пароля и в случае успеха
// устанавливает его, если вызов выполнен из формы СменаПароля.
// 
// Параметры:
//  Параметры - Структура:
//   * Пользователь - СправочникСсылка.Пользователи
//                  - СправочникСсылка.ВнешниеПользователи - при вызове из формы СменаПароля.
//                  - СправочникОбъект.Пользователи
//                  - СправочникОбъект.ВнешниеПользователи - при записи объекта.
//
//   * НовыйПароль  - Строка - пароль, который планируется установить пользователю ИБ.
//   * СтарыйПароль - Строка - пароль, который установлен пользователю ИБ (для проверки).
//
//   * ПриВходеВПрограмму    - Булево - может быть Истина при вызове из формы СменаПароля.
//   * ТолькоПроверить       - Булево - может быть Истина при вызове из формы СменаПароля.
//   * СтарыйПарольСовпадает - Булево - возвращаемое значение. Если Ложь, значит не совпадает.
//
//   * ПарольПользователяСервиса - Строка - пароль текущего пользователя при вызове
//                                          из формы СменаПароля, сбрасывается при ошибке.
//
// Возвращаемое значение:
//  Строка - текст ошибки, если не пустая строка.
//
Функция ОбработатьНовыйПароль(Параметры) Экспорт
	
	НовыйПароль  = Параметры.НовыйПароль;
	СтарыйПароль = Параметры.СтарыйПароль;
	
	ДополнительныеПараметры = Новый Структура;
	
	Если ТипЗнч(Параметры.Пользователь) = Тип("СправочникОбъект.Пользователи")
	 Или ТипЗнч(Параметры.Пользователь) = Тип("СправочникОбъект.ВнешниеПользователи") Тогда
		
		СсылкаОбъекта = Параметры.Пользователь.Ссылка;
		Пользователь  = СсылкаОбъекта(Параметры.Пользователь);
		ВызовИзФормыСменаПароля = Ложь;
		
		Если ТипЗнч(Параметры.Пользователь) = Тип("СправочникОбъект.Пользователи")
		   И Параметры.Пользователь.Служебный Тогда
			
			ДополнительныеПараметры.Вставить("ЭтоСлужебныйПользователь");
		КонецЕсли;
	Иначе
		СсылкаОбъекта = Параметры.Пользователь;
		Пользователь  = Параметры.Пользователь;
		ВызовИзФормыСменаПароля = Истина;
	КонецЕсли;
	
	Параметры.Вставить("СтарыйПарольСовпадает", Ложь);
	
	Если Не ВозможноИзменитьПароль(СсылкаОбъекта, ДополнительныеПараметры) Тогда
		Возврат ДополнительныеПараметры.ТекстОшибки;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	Если ДополнительныеПараметры.ЭтоТекущийПользовательИБ
	   И ДополнительныеПараметры.ПарольУстановлен
	   И (СтарыйПароль <> Неопределено) Тогда
		
		Параметры.СтарыйПарольСовпадает = СтарыйПарольСовпадаетССохраненным(
			СтарыйПароль, ДополнительныеПараметры.ИдентификаторПользователяИБ);
		
		Если Не Параметры.СтарыйПарольСовпадает Тогда
			Возврат НСтр("ru = 'Старый пароль указан неверно.'");
		КонецЕсли;
	КонецЕсли;
	
	ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
		ДополнительныеПараметры.ИдентификаторПользователяИБ);
	Если ТипЗнч(ПользовательИБ) <> Тип("ПользовательИнформационнойБазы") Тогда
		ПользовательИБ = ПользователиИнформационнойБазы.СоздатьПользователя();
	КонецЕсли;
	УстановитьПолитикуПаролей(ПользовательИБ,
		ТипЗнч(Пользователь) = Тип("СправочникСсылка.ВнешниеПользователи"));
	ТекстОшибкиПароля = ОшибкаСоответствияПароляТребованиям(НовыйПароль, ПользовательИБ);
	
	УстановитьПривилегированныйРежим(Ложь);
	
	Если ЗначениеЗаполнено(ТекстОшибкиПароля) Тогда
		Возврат ТекстОшибкиПароля;
	КонецЕсли;
	
	Если ВызовИзФормыСменаПароля И Параметры.ТолькоПроверить Тогда
		Возврат "";
	КонецЕсли;
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить(Метаданные.НайтиПоТипу(ТипЗнч(Пользователь)).ПолноеИмя());
	ЭлементБлокировки.УстановитьЗначение("Ссылка", Пользователь);
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.СведенияОПользователях");
	ЭлементБлокировки.УстановитьЗначение("Пользователь", Пользователь);
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		
		Если ВызовИзФормыСменаПароля Тогда
			ОписаниеПользователяИБ = Новый Структура;
			ОписаниеПользователяИБ.Вставить("Действие", "Записать");
			ОписаниеПользователяИБ.Вставить("Пароль", НовыйПароль);
			
			ТекущийОбъект = Пользователь.ПолучитьОбъект();
			ТекущийОбъект.ДополнительныеСвойства.Вставить("ОписаниеПользователяИБ",
				ОписаниеПользователяИБ);
			
			Если Параметры.ПриВходеВПрограмму Тогда
				ТекущийОбъект.ДополнительныеСвойства.Вставить("СменаПароляПриВходеВПрограмму");
			КонецЕсли;
			Если ОбщегоНазначения.РазделениеВключено() Тогда
				Если ДополнительныеПараметры.ЭтоТекущийПользовательИБ Тогда
					ТекущийОбъект.ДополнительныеСвойства.Вставить("ПарольПользователяСервиса",
						СтарыйПароль);
				Иначе
					ТекущийОбъект.ДополнительныеСвойства.Вставить("ПарольПользователяСервиса",
						Параметры.ПарольПользователяСервиса);
				КонецЕсли;
				ТекущийОбъект.ДополнительныеСвойства.Вставить("СинхронизироватьССервисом", Истина);
			КонецЕсли;
			Попытка
				ТекущийОбъект.Записать();
			Исключение
				Параметры.ПарольПользователяСервиса = Неопределено;
				ВызватьИсключение;
			КонецПопытки;
		Иначе
			НаборЗаписей = РегистрыСведений.СведенияОПользователях.СоздатьНаборЗаписей();
			НаборЗаписей.Отбор.Пользователь.Установить(Пользователь);
			НаборЗаписей.Прочитать();
			Записать = Ложь;
			Если НаборЗаписей.Количество() = 0 Тогда
				СведенияОПользователе = НаборЗаписей.Добавить();
				СведенияОПользователе.Пользователь = Пользователь;
				Записать = Истина;
			Иначе
				СведенияОПользователе = НаборЗаписей[0];
			КонецЕсли;
			Если Параметры.Пользователь.ДополнительныеСвойства.Свойство("СменаПароляПриВходеВПрограмму") Тогда
				СведенияОПользователе.ПотребоватьСменуПароляПриВходе = Ложь;
				Записать = Истина;
			КонецЕсли;
			Если ОбщегоНазначения.РазделениеВключено() Тогда
				СведенияОПользователе.УдалитьДатаНачалаИспользованияПароля = НачалоДня(ТекущаяДатаСеанса());
				Записать = Истина;
			КонецЕсли;
			Если Записать Тогда
				НаборЗаписей.Записать();
			КонецЕсли;
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		Если ВызовИзФормыСменаПароля Тогда
			ЗаписьЖурналаРегистрации(
				НСтр("ru = 'Пользователи.Ошибка смены пароля'",
				     ОбщегоНазначения.КодОсновногоЯзыка()),
				УровеньЖурналаРегистрации.Ошибка,
				Метаданные.НайтиПоТипу(ТипЗнч(Пользователь)),
				Пользователь,
				СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось сменить пароль пользователя ""%1"" по причине:
					           |%2'"),
					Пользователь, ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)));
			Параметры.Вставить("ОшибкаЗаписанаВЖурналРегистрации");
		КонецЕсли;
		ВызватьИсключение;
	КонецПопытки;
	
	Возврат "";
	
КонецФункции

// Для формы СменаПароля и функции НовыйПарольМожноУстановить.
//
// Возвращаемое значение:
//  Строка
//
Функция ПодсказкаДляНовогоПароля() Экспорт
	
	Возврат
		НСтр("ru = 'Надежный пароль:
		           |- имеет не менее 7 символов;
		           |- содержит любые 3 из 4-х типов символов: заглавные
		           |  буквы, строчные буквы, цифры, специальные символы;
		           |- не совпадает с именем (для входа).'");
	
КонецФункции

// Для форм элементов справочников Пользователи и ВнешниеПользователи.
//
// Возвращаемое значение:
//  Строка
//
Функция ПодсказкаПотребоватьСменуПароляПриВходе(ДляВнешнихПользователей) Экспорт
	
	ЭтоПолноправныйПользователь = Пользователи.ЭтоПолноправныйПользователь(, Ложь);
	
	Если Не ЭтоПолноправныйПользователь Тогда
		Подсказка =
			НСтр("ru = 'Требования к длине и сложности пароля задаются отдельно.
			           |За подробностями обратитесь к администратору.'");
		Возврат Новый ФорматированнаяСтрока(Подсказка);
	КонецЕсли;
	
	Если Не Пользователи.ОбщиеНастройкиВходаИспользуются() Тогда
		Подсказка =
			НСтр("ru = 'Требования к длине и сложности пароля задаются отдельно.
			           |См. Параметры информационной базы в конфигураторе в меню Администрирование.'");
		Возврат Новый ФорматированнаяСтрока(Подсказка);
	КонецЕсли;
	
	ЕстьРазделАдминистрирование = Метаданные.Подсистемы.Найти("Администрирование") <> Неопределено;
	
	Если ДляВнешнихПользователей Тогда
		Если ЕстьРазделАдминистрирование Тогда
			Подсказка =
				НСтр("ru = 'Требования к длине и сложности пароля задаются отдельно.
				           |См. <a href = ""%1"">Настройки входа</a> на вкладке <b>Для внешних пользователей</b>
				           |в разделе <b>Администрирование</b>, пункт <b>Настройки пользователей и прав</b>.'");
		Иначе
			Подсказка =
				НСтр("ru = 'Требования к длине и сложности пароля задаются отдельно.
				           |См. <a href = ""%1"">Настройки входа</a> на вкладке <b>Для внешних пользователей</b>.'");
		КонецЕсли;
	Иначе
		Если ЕстьРазделАдминистрирование Тогда
			Подсказка =
				НСтр("ru = 'Требования к длине и сложности пароля задаются отдельно.
				           |См. <a href = ""%1"">Настройки входа</a> на вкладке <b>Для пользователей</b>
				           |в разделе <b>Администрирование</b>, пункт <b>Настройки пользователей и прав</b>.'");
		Иначе
			Подсказка =
				НСтр("ru = 'Требования к длине и сложности пароля задаются отдельно.
				           |См. <a href = ""%1"">Настройки входа</a> на вкладке <b>Для пользователей</b>.'");
		КонецЕсли;
	КонецЕсли;
	
	Возврат СтроковыеФункции.ФорматированнаяСтрока(Подсказка,
		"НастройкиВходаПользователей");
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции работы с пользователями.

// Только для внутреннего использования.
//
// Возвращаемое значение:
//  СправочникСсылка.Пользователи
//  СправочникСсылка.ВнешниеПользователи
//
Функция АвторизованныйПользователь() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		ТекстОшибки = ТекстСообщенияТекущийПользовательНедоступенВСеансеБезРазделителей();
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Возврат ?(ЗначениеЗаполнено(ПараметрыСеанса.ТекущийПользователь),
		ПараметрыСеанса.ТекущийПользователь,
		ПараметрыСеанса.ТекущийВнешнийПользователь);
	
КонецФункции

// Возвращает сохраняемое значение пароля для указанного пароля.
//
// Параметры:
//  Пароль    - Строка - пароль для которого нужно получить сохраняемое значение.
//  ДляЗаписи - Булево - если Истина, тогда для пустого пароля будет непустой результат.
//
// Возвращаемое значение:
//  Строка - сохраняемое значение пароля в формате свойства СохраняемоеЗначениеПароля
//           типа ПользовательИнформационнойБазы.
//
Функция СохраняемоеЗначениеСтрокиПароля(Пароль, ДляЗаписи = Ложь) Экспорт
	
	Если Пароль = "" И Не ДляЗаписи Тогда
		СохраняемоеЗначениеПароля = "";
	Иначе
		ХешированиеДанных = Новый ХешированиеДанных(ХешФункция.SHA1);
		ХешированиеДанных.Добавить(Пароль);
		
		СохраняемоеЗначениеПароля = Base64Строка(ХешированиеДанных.ХешСумма);
		
		ХешированиеДанных = Новый ХешированиеДанных(ХешФункция.SHA1);
		ХешированиеДанных.Добавить(ВРег(Пароль));
		
		СохраняемоеЗначениеПароля = СохраняемоеЗначениеПароля + ","
			+ Base64Строка(ХешированиеДанных.ХешСумма);
	КонецЕсли;
	
	Возврат СохраняемоеЗначениеПароля;
	
КонецФункции

// Сравнивает старый пароль с ранее сохраненным паролем
// для пользователя ИБ без учета контроля сложности пароля.
//
// Параметры:
//  Пароль                      - Строка - старый пароль, который нужно сравнить.
//
//  ИдентификаторПользователяИБ - УникальныйИдентификатор - пользователь ИБ для которого
//                                нужно проверить старый пароль.
//
// Возвращаемое значение:
//  Булево - Истина, если пароль совпадает без учета контроля сложности пароля.
//
Функция СтарыйПарольСовпадаетССохраненным(Пароль, ИдентификаторПользователяИБ) Экспорт
	
	Если ТипЗнч(ИдентификаторПользователяИБ) <> Тип("УникальныйИдентификатор") Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
		ИдентификаторПользователяИБ);
	
	Если ТипЗнч(ПользовательИБ) <> Тип("ПользовательИнформационнойБазы") Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Возврат ХешСуммаПароляСовпадает(СохраняемоеЗначениеСтрокиПароля(Пароль),
		ПользовательИБ.СохраняемоеЗначениеПароля);
	
КонецФункции

// Проверяет что у первого и второго паролей совпадают первые или вторые хеш-суммы.
//
// Параметры:
//  СохраняемоеЗначениеПервогоПароля - Строка - содержит хеш-суммы сохраняемого пароля в формате
//                                       одноименного свойства типа ПользовательИнформационнойБазы.
//
//  СохраняемоеЗначениеВторогоПароля - Строка - то же, что и СохраняемоеЗначениеВторогоПароля.
//
//
Функция ХешСуммаПароляСовпадает(СохраняемоеЗначениеПервогоПароля, СохраняемоеЗначениеВторогоПароля)
	
	Если СохраняемоеЗначениеПервогоПароля = СохраняемоеЗначениеВторогоПароля Тогда
		Возврат Истина;
	КонецЕсли;
	
	ХешСуммыПервогоПароля = СтрРазделить(СохраняемоеЗначениеПервогоПароля, ",", Ложь);
	Если ХешСуммыПервогоПароля.Количество() <> 2 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ХешСуммыВторогоПароля = СтрРазделить(СохраняемоеЗначениеВторогоПароля, ",", Ложь);
	Если ХешСуммыВторогоПароля.Количество() <> 2 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Возврат ХешСуммыПервогоПароля[0] = ХешСуммыВторогоПароля[0]
		Или ХешСуммыПервогоПароля[1] = ХешСуммыВторогоПароля[1];
	
КонецФункции

// Возвращает текущий уровень доступа на изменение свойств пользователя ИБ.
// 
// Параметры:
//  ОписаниеОбъекта - СправочникОбъект.Пользователи
//                  - СправочникОбъект.ВнешниеПользователи
//                  - ДанныеФормыСтруктура - созданная из объектов указанных выше.
//
//  ПараметрыОбработки - Неопределено - получить данные из описания объекта,
//                       иначе взять уже готовые данные из параметров обработки.
//
// Возвращаемое значение:
//  Структура:
//   * АдминистраторСистемы       - Булево - любые действия с любым пользователем и его пользователем ИБ.
//   * ПолныеПрава                - Булево - то же, АдминистраторСистемы, исключая администраторов системы.
//   * УправлениеСписком          - Булево - добавление новых пользователей и изменение существующих:
//                                  а) для пользователей, которым еще не разрешался вход в программу (новых),
//                                     можно настроить любые свойства, кроме включения входа в программу,
//                                  б) для пользователей, которым уже разрешался вход в программу,
//                                     можно настроить любые свойства, кроме включения входа в программу
//                                     и настройки аутентификации (см. ниже).
//   * ИзменениеРазрешенияНаВход  - Булево - изменение флажка "Вход в программу разрешен".
//   * ОтключениеРазрешенияНаВход - Булево - снятие    флажка "Вход в программу разрешен".
//   * НастройкиДляВхода          - Булево - изменение свойств пользователя ИБ Имя, ПользовательОС и
//                                    свойств элемента справочника АутентификацияСтандартная, АутентификацияOpenID,
//                                    АутентификацияOpenIDConnect, АутентификацияТокеномДоступа,
//                                    АутентификацияОС и Роли (если нет запрета редактирования при внедрении).
//   * ИзменениеТекущего          - Булево - изменение свойств текущего пользователя Пароль и Язык.
//   * НетДоступа                 - Булево - нет ни одного из указанных выше уровней доступа.
//
Функция УровеньДоступаКСвойствамПользователя(ОписаниеОбъекта, ПараметрыОбработки = Неопределено) Экспорт
	
	УровеньДоступа = Новый Структура;
	
	// Администратор системы (системные данные).
	УровеньДоступа.Вставить("АдминистраторСистемы", Пользователи.ЭтоПолноправныйПользователь(, Истина));
	
	// Полноправный пользователь (пользовательские данные).
	УровеньДоступа.Вставить("ПолныеПрава", Пользователи.ЭтоПолноправныйПользователь());
	
	Если ТипЗнч(ОписаниеОбъекта.Ссылка) = Тип("СправочникСсылка.Пользователи") Тогда
		// Ответственный за список пользователей.
		УровеньДоступа.Вставить("УправлениеСписком",
			ПравоДоступа("Добавление", Метаданные.Справочники.Пользователи)
			И (УровеньДоступа.ПолныеПрава
			   Или Не Пользователи.ЭтоПолноправныйПользователь(ОписаниеОбъекта.Ссылка)));
		// Пользователь текущего пользователя ИБ.
		УровеньДоступа.Вставить("ИзменениеТекущего",
			УровеньДоступа.ПолныеПрава
			Или ПравоДоступа("Изменение", Метаданные.Справочники.Пользователи)
			  И ОписаниеОбъекта.Ссылка = Пользователи.АвторизованныйПользователь());
		
	ИначеЕсли ТипЗнч(ОписаниеОбъекта.Ссылка) = Тип("СправочникСсылка.ВнешниеПользователи") Тогда
		// Ответственный за список внешних пользователей.
		УровеньДоступа.Вставить("УправлениеСписком",
			ПравоДоступа("Добавление", Метаданные.Справочники.ВнешниеПользователи)
			И (УровеньДоступа.ПолныеПрава
			   Или Не Пользователи.ЭтоПолноправныйПользователь(ОписаниеОбъекта.Ссылка)));
		// Внешний пользователь текущего пользователя ИБ.
		УровеньДоступа.Вставить("ИзменениеТекущего",
			УровеньДоступа.ПолныеПрава
			Или ПравоДоступа("Изменение", Метаданные.Справочники.ВнешниеПользователи)
			  И ОписаниеОбъекта.Ссылка = Пользователи.АвторизованныйПользователь());
	КонецЕсли;
	
	Если ПараметрыОбработки = Неопределено Тогда
		УстановитьПривилегированныйРежим(Истина);
		Если ЗначениеЗаполнено(ОписаниеОбъекта.ИдентификаторПользователяИБ) Тогда
			ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
				ОписаниеОбъекта.ИдентификаторПользователяИБ);
		Иначе
			ПользовательИБ = Неопределено;
		КонецЕсли;
		ПользовательБезНастроекДляВходаИлиПодготовлен =
			    ПользовательИБ = Неопределено
			Или ОписаниеОбъекта.Подготовлен
			    И Не Пользователи.ВходВПрограммуРазрешен(ПользовательИБ);
		УстановитьПривилегированныйРежим(Ложь);
	Иначе
		ПользовательБезНастроекДляВходаИлиПодготовлен =
			    Не ПараметрыОбработки.СтарыйПользовательИБСуществует
			Или ПараметрыОбработки.СтарыйПользователь.Подготовлен
			    И Не Пользователи.ВходВПрограммуРазрешен(ПараметрыОбработки.ОписаниеСтарогоПользователяИБ);
	КонецЕсли;
	
	УровеньДоступа.Вставить("ИзменениеРазрешенияНаВход",
		    УровеньДоступа.АдминистраторСистемы
		Или УровеньДоступа.ПолныеПрава
		  И Не Пользователи.ЭтоПолноправныйПользователь(ОписаниеОбъекта.Ссылка, Истина));
	
	УровеньДоступа.Вставить("ОтключениеРазрешенияНаВход",
		    УровеньДоступа.АдминистраторСистемы
		Или УровеньДоступа.ПолныеПрава
		  И Не Пользователи.ЭтоПолноправныйПользователь(ОписаниеОбъекта.Ссылка, Истина)
		Или УровеньДоступа.УправлениеСписком);
	
	УровеньДоступа.Вставить("НастройкиДляВхода",
		    УровеньДоступа.АдминистраторСистемы
		Или УровеньДоступа.ПолныеПрава
		  И Не Пользователи.ЭтоПолноправныйПользователь(ОписаниеОбъекта.Ссылка, Истина)
		Или УровеньДоступа.УправлениеСписком
		  И ПользовательБезНастроекДляВходаИлиПодготовлен);
	
	УровеньДоступа.Вставить("НетДоступа",
		  НЕ УровеньДоступа.АдминистраторСистемы
		И НЕ УровеньДоступа.ПолныеПрава
		И НЕ УровеньДоступа.УправлениеСписком
		И НЕ УровеньДоступа.ИзменениеТекущего
		И НЕ УровеньДоступа.НастройкиДляВхода);
	
	Возврат УровеньДоступа;
	
КонецФункции

// Проверяет, что указанный пользователь имеем более высокий уровень доступа, чем текущий пользователь.
// 
// Параметры:
//  ОписаниеПользователя - СправочникСсылка.Пользователи
//                       - СправочникСсылка.ВнешниеПользователи
// 
// ТекущийУровеньДоступа - см. УровеньДоступаКСвойствамПользователя
// 
// Возвращаемое значение:
//  Булево
//
Функция УровеньДоступаПользователяВыше(ОписаниеПользователя, ТекущийУровеньДоступа) Экспорт
	
	Если ТипЗнч(ОписаниеПользователя) = Тип("СправочникСсылка.Пользователи")
	 Или ТипЗнч(ОписаниеПользователя) = Тип("СправочникСсылка.ВнешниеПользователи") Тогда
		
		Возврат Пользователи.ЭтоПолноправныйПользователь(ОписаниеПользователя, Истина, Ложь)
		      И Не ТекущийУровеньДоступа.АдминистраторСистемы
		    Или Пользователи.ЭтоПолноправныйПользователь(ОписаниеПользователя, Ложь, Ложь)
		      И Не ТекущийУровеньДоступа.ПолныеПрава;
	Иначе
		Возврат ОписаниеПользователя.Роли.Найти("АдминистраторСистемы") <> Неопределено
		      И Не ТекущийУровеньДоступа.АдминистраторСистемы
		    Или ОписаниеПользователя.Роли.Найти("ПолныеПрава") <> Неопределено
		      И Не ТекущийУровеньДоступа.ПолныеПрава;
	КонецЕсли;
	
КонецФункции

// Вызывается ПередЗаписью пользователя или внешнего пользователя.
//
// Параметры:
//  ПользовательОбъект - СправочникОбъект.Пользователи
//                     - СправочникОбъект.ВнешниеПользователи
//  ПараметрыОбработки - Структура
//  УдалениеПользователяИзСправочника - Булево
//
Процедура НачатьОбработкуПользователяИБ(ПользовательОбъект,
                                        ПараметрыОбработки,
                                        УдалениеПользователяИзСправочника = Ложь) Экспорт
	
	ПараметрыОбработки = Новый Структура;
	ДополнительныеСвойства = ПользовательОбъект.ДополнительныеСвойства;
	
	ПараметрыОбработки.Вставить("УдалениеПользователяИзСправочника", УдалениеПользователяИзСправочника);
	ПараметрыОбработки.Вставить("ТекстСообщенияНедостаточноПрав",
		НСтр("ru = 'Недостаточно прав для изменения пользователя информационной базы.'"));
	
	Если ДополнительныеСвойства.Свойство("ЗначениеКопирования")
	   И ЗначениеЗаполнено(ДополнительныеСвойства.ЗначениеКопирования)
	   И ТипЗнч(ДополнительныеСвойства.ЗначениеКопирования) = ТипЗнч(ПользовательОбъект.Ссылка) Тогда
		
		ПараметрыОбработки.Вставить("ЗначениеКопирования", ДополнительныеСвойства.ЗначениеКопирования);
	КонецЕсли;
	
	// Реквизиты справочника, которые устанавливаются автоматически (проверка неизменности).
	АвтоРеквизиты = Новый Структура;
	АвтоРеквизиты.Вставить("ИдентификаторПользователяИБ");
	АвтоРеквизиты.Вставить("УдалитьСвойстваПользователяИБ");
	ПараметрыОбработки.Вставить("АвтоРеквизиты", АвтоРеквизиты);
	
	// Реквизиты справочника, которые не допускается изменять в подписках на события (проверка начальных значений).
	БлокируемыеРеквизиты = Новый Структура;
	БлокируемыеРеквизиты.Вставить("Служебный", Ложь); // Значение для внешнего пользователя.
	БлокируемыеРеквизиты.Вставить("ПометкаУдаления");
	БлокируемыеРеквизиты.Вставить("Недействителен");
	БлокируемыеРеквизиты.Вставить("Подготовлен");
	ПараметрыОбработки.Вставить("БлокируемыеРеквизиты", БлокируемыеРеквизиты);
	
	ЗапомнитьСвойстваПользователя(ПользовательОбъект, ПараметрыОбработки);
	
	УровеньДоступа = УровеньДоступаКСвойствамПользователя(ПользовательОбъект, ПараметрыОбработки);
	ПараметрыОбработки.Вставить("УровеньДоступа", УровеньДоступа);
	
	// ПередНачаломОбработкиПользователяИБ - поддержка модели сервиса.
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ПользователиВМоделиСервиса") Тогда
		МодульПользователиСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ПользователиСлужебныйВМоделиСервиса");
		МодульПользователиСлужебныйВМоделиСервиса.ПередНачаломОбработкиПользователяИБ(ПользовательОбъект, ПараметрыОбработки);
	КонецЕсли;
	
	Если ПараметрыОбработки.СтарыйПользователь.Подготовлен <> ПользовательОбъект.Подготовлен
	   И Не УровеньДоступа.ИзменениеРазрешенияНаВход Тогда
		
		ВызватьИсключение ПараметрыОбработки.ТекстСообщенияНедостаточноПрав;
	КонецЕсли;
	
	// Поддержка интерактивной пометки удаления и группового изменения реквизитов ПометкаУдаления, Недействителен.
	Если ПараметрыОбработки.СтарыйПользовательИБСуществует
	   И Пользователи.ВходВПрограммуРазрешен(ПараметрыОбработки.ОписаниеСтарогоПользователяИБ)
	   И Не ДополнительныеСвойства.Свойство("ОписаниеПользователяИБ")
	   И (  ПараметрыОбработки.СтарыйПользователь.ПометкаУдаления = Ложь
	      И ПользовательОбъект.ПометкаУдаления = Истина
	    Или ПараметрыОбработки.СтарыйПользователь.Недействителен = Ложь
	      И ПользовательОбъект.Недействителен  = Истина) Тогда
		
		ДополнительныеСвойства.Вставить("ОписаниеПользователяИБ", Новый Структура);
		ДополнительныеСвойства.ОписаниеПользователяИБ.Вставить("Действие", "Записать");
		ДополнительныеСвойства.ОписаниеПользователяИБ.Вставить("ВходВПрограммуРазрешен", Ложь);
	КонецЕсли;
	
	// Поддержка обновления полного имени пользователя ИБ при изменении наименования.
	Если ПараметрыОбработки.СтарыйПользовательИБСуществует
	   И Не ДополнительныеСвойства.Свойство("ОписаниеПользователяИБ")
	   И ПараметрыОбработки.ОписаниеСтарогоПользователяИБ.ПолноеИмя
	     <> ПользовательОбъект.Наименование Тогда
		
		ДополнительныеСвойства.Вставить("ОписаниеПользователяИБ", Новый Структура);
		ДополнительныеСвойства.ОписаниеПользователяИБ.Вставить("Действие", "Записать");
	КонецЕсли;
	
	Если НЕ ДополнительныеСвойства.Свойство("ОписаниеПользователяИБ") Тогда
		Если УровеньДоступа.УправлениеСписком
		   И Не ПараметрыОбработки.СтарыйПользовательИБСуществует
		   И ЗначениеЗаполнено(ПользовательОбъект.ИдентификаторПользователяИБ) Тогда
			// Очистка идентификатора пользователя ИБ.
			ПользовательОбъект.ИдентификаторПользователяИБ = Неопределено;
			ПараметрыОбработки.АвтоРеквизиты.ИдентификаторПользователяИБ =
				ПользовательОбъект.ИдентификаторПользователяИБ;
		КонецЕсли;
		Возврат;
	КонецЕсли;
	ОписаниеПользователяИБ = ДополнительныеСвойства.ОписаниеПользователяИБ;
	
	Если НЕ ОписаниеПользователяИБ.Свойство("Действие") Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка при записи пользователя ""%1"".
			           |В параметре %2 не указано свойство %3.'"),
			ПользовательОбъект.Ссылка, "ОписаниеПользователяИБ", "Действие");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если ОписаниеПользователяИБ.Действие <> "Записать"
	   И ОписаниеПользователяИБ.Действие <> "Удалить" Тогда
		
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка при записи пользователя ""%1"".
			           |В параметре %2 указано
			           |неверное значение ""%3"" свойства %4.'"),
			ПользовательОбъект.Ссылка,
			"ОписаниеПользователяИБ",
			ОписаниеПользователяИБ.Действие,
			"Действие");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	ПараметрыОбработки.Вставить("Действие", ОписаниеПользователяИБ.Действие);
	
	ИнтеграцияПодсистемБСП.ПриНачалеОбработкиПользователяИБ(ПараметрыОбработки, ОписаниеПользователяИБ);
	
	УстановитьПривилегированныйРежим(Истина);
	
	Если ОписаниеПользователяИБ.Действие = "Записать"
	   И ОписаниеПользователяИБ.Свойство("УникальныйИдентификатор")
	   И ЗначениеЗаполнено(ОписаниеПользователяИБ.УникальныйИдентификатор)
	   И ОписаниеПользователяИБ.УникальныйИдентификатор
	     <> ПараметрыОбработки.СтарыйПользователь.ИдентификаторПользователяИБ Тогда
		
		ПараметрыОбработки.Вставить("УстановкаПользователяИБ");
		
		Если ПараметрыОбработки.СтарыйПользовательИБСуществует Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Ошибка при записи пользователя ""%1"".
				           |Нельзя сопоставить пользователя ИБ с пользователем в справочнике,
				           |с которым уже сопоставлен другой пользователем ИБ.'"),
				ПользовательОбъект.Наименование);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		НайденныйПользователь = Неопределено;
		
		Если ПользовательПоИдентификаторуСуществует(
			ОписаниеПользователяИБ.УникальныйИдентификатор,
			ПользовательОбъект.Ссылка,
			НайденныйПользователь) Тогда
			
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Ошибка при записи пользователя ""%1"".
				           |Нельзя сопоставить пользователя ИБ с этим пользователем в справочнике,
				           |так как он уже сопоставлен с другим пользователем в справочнике
				           |""%2"".'"),
				НайденныйПользователь,
				ПользовательОбъект.Наименование);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		Если Не УровеньДоступа.ПолныеПрава Тогда
			ВызватьИсключение ПараметрыОбработки.ТекстСообщенияНедостаточноПрав;
		КонецЕсли;
		
		Если Не ПараметрыОбработки.Свойство("Действие") Тогда
			ПользовательОбъект.ИдентификаторПользователяИБ = ОписаниеПользователяИБ.УникальныйИдентификатор;
			// Обновление значения реквизита контролируемого при записи.
			ПараметрыОбработки.АвтоРеквизиты.ИдентификаторПользователяИБ =
				ПользовательОбъект.ИдентификаторПользователяИБ;
		КонецЕсли;
	КонецЕсли;
	
	Если Не ПараметрыОбработки.Свойство("Действие") Тогда
		Возврат;
	КонецЕсли;
	
	Если УровеньДоступа.НетДоступа Тогда
		ВызватьИсключение ПараметрыОбработки.ТекстСообщенияНедостаточноПрав;
	КонецЕсли;
	
	Если ОписаниеПользователяИБ.Действие = "Удалить" Тогда
		
		Если Не УровеньДоступа.ИзменениеРазрешенияНаВход Тогда
			ВызватьИсключение ПараметрыОбработки.ТекстСообщенияНедостаточноПрав;
		КонецЕсли;
		
	ИначеЕсли Не УровеньДоступа.УправлениеСписком Тогда // Действие = "Записать"
		
		Если Не УровеньДоступа.ИзменениеТекущего
		 Или Не ПараметрыОбработки.СтарыйПользовательИБТекущий Тогда
			
			ВызватьИсключение ПараметрыОбработки.ТекстСообщенияНедостаточноПрав;
		КонецЕсли;
	КонецЕсли;
	
	Если ОписаниеПользователяИБ.Действие = "Записать" Тогда
		
		// Проверка прав на изменение пользователей с широким доступом.
		Если ПараметрыОбработки.СтарыйПользовательИБСуществует
		   И УровеньДоступаПользователяВыше(ПараметрыОбработки.ОписаниеСтарогоПользователяИБ, УровеньДоступа) Тогда
			
			ВызватьИсключение ПараметрыОбработки.ТекстСообщенияНедостаточноПрав;
		КонецЕсли;
		
		// Проверка попытки изменения недоступных свойств.
		Если Не УровеньДоступа.ПолныеПрава Тогда
			ДопустимыеСвойства = Новый Структура;
			ДопустимыеСвойства.Вставить("УникальныйИдентификатор"); // Проверено выше.
			
			Если УровеньДоступа.ИзменениеТекущего Тогда
				ДопустимыеСвойства.Вставить("АдресЭлектроннойПочты");
				ДопустимыеСвойства.Вставить("Пароль");
				ДопустимыеСвойства.Вставить("Язык");
			КонецЕсли;
			
			Если УровеньДоступа.УправлениеСписком Тогда
				ДопустимыеСвойства.Вставить("ПолноеИмя");
				ДопустимыеСвойства.Вставить("АдресЭлектроннойПочты");
				ДопустимыеСвойства.Вставить("ПоказыватьВСпискеВыбора");
				ДопустимыеСвойства.Вставить("ЗапрещеноИзменятьПароль");
				ДопустимыеСвойства.Вставить("ЗапрещеноВосстанавливатьПароль");
				ДопустимыеСвойства.Вставить("Язык");
				ДопустимыеСвойства.Вставить("РежимЗапуска");
			КонецЕсли;
			
			Если УровеньДоступа.НастройкиДляВхода Тогда
				ДопустимыеСвойства.Вставить("Имя");
				ДопустимыеСвойства.Вставить("АутентификацияСтандартная");
				ДопустимыеСвойства.Вставить("Пароль");
				ДопустимыеСвойства.Вставить("АутентификацияOpenID");
				ДопустимыеСвойства.Вставить("АутентификацияOpenIDConnect");
				ДопустимыеСвойства.Вставить("АутентификацияТокеномДоступа");
				ДопустимыеСвойства.Вставить("АутентификацияОС");
				ДопустимыеСвойства.Вставить("ПользовательОС");
				ДопустимыеСвойства.Вставить("Роли");
			КонецЕсли;
			
			ВсеСвойства = Пользователи.НовоеОписаниеПользователяИБ();
			
			Для Каждого КлючИЗначение Из ОписаниеПользователяИБ Цикл
				
				Если ВсеСвойства.Свойство(КлючИЗначение.Ключ)
				   И Не ДопустимыеСвойства.Свойство(КлючИЗначение.Ключ) Тогда
					
					ВызватьИсключение ПараметрыОбработки.ТекстСообщенияНедостаточноПрав;
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		
		ЗаписатьПользователяИБ(ПользовательОбъект, ПараметрыОбработки);
	Иначе
		УдалитьПользователяИБ(ПользовательОбъект, ПараметрыОбработки);
	КонецЕсли;
	
	// Обновление значения реквизита контролируемого при записи.
	ПараметрыОбработки.АвтоРеквизиты.ИдентификаторПользователяИБ =
		ПользовательОбъект.ИдентификаторПользователяИБ;
	
	ОписаниеНовогоПользователяИБ = Пользователи.СвойстваПользователяИБ(ПользовательОбъект.ИдентификаторПользователяИБ);
	ПараметрыОбработки.Вставить("НовыйПользовательИБСуществует", ОписаниеНовогоПользователяИБ <> Неопределено);
	ПараметрыОбработки.Вставить("НовыйПользовательИБ",
		ПользовательИБПоИдентификатору(ПользовательОбъект.ИдентификаторПользователяИБ));
	
	Если ОписаниеНовогоПользователяИБ <> Неопределено Тогда
		ПараметрыОбработки.Вставить("ОписаниеНовогоПользователяИБ", ОписаниеНовогоПользователяИБ);
		
		// Проверка прав на изменение пользователей с широким доступом.
		Если ПараметрыОбработки.СтарыйПользовательИБСуществует
		   И УровеньДоступаПользователяВыше(ПараметрыОбработки.ОписаниеНовогоПользователяИБ, УровеньДоступа) Тогда
			
			ВызватьИсключение ПараметрыОбработки.ТекстСообщенияНедостаточноПрав;
		КонецЕсли;
	КонецЕсли;
	
	// ПослеНачалаОбработкиПользователяИБ - поддержка модели сервиса.
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ПользователиВМоделиСервиса") Тогда
		МодульПользователиСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ПользователиСлужебныйВМоделиСервиса");
		МодульПользователиСлужебныйВМоделиСервиса.ПослеНачалаОбработкиПользователяИБ(ПользовательОбъект, ПараметрыОбработки);
	КонецЕсли;
	
	Если ПараметрыОбработки.Свойство("СозданиеАдминистратора") Тогда
		УстановитьПривилегированныйРежим(Истина);
		ИнтеграцияПодсистемБСП.ПриСозданииАдминистратора(СсылкаОбъекта(ПользовательОбъект),
			ПараметрыОбработки.СозданиеАдминистратора);
		УстановитьПривилегированныйРежим(Ложь);
	КонецЕсли;
	
КонецПроцедуры

// Вызывается ПриЗаписи Пользователя или Внешнего пользователя.
//
// Параметры:
//  ПользовательОбъект - СправочникОбъект.Пользователи
//                     - СправочникОбъект.ВнешниеПользователи
//  ПараметрыОбработки - Структура
//
Процедура ЗавершитьОбработкуПользователяИБ(ПользовательОбъект, ПараметрыОбработки) Экспорт
	
	ПроверитьИзмененияРеквизитовПользователя(ПользовательОбъект, ПараметрыОбработки);
	
	// ПередЗавершениемОбработкиПользователяИБ - поддержка модели сервиса.
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ПользователиВМоделиСервиса") Тогда
		МодульПользователиСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ПользователиСлужебныйВМоделиСервиса");
		МодульПользователиСлужебныйВМоделиСервиса.ПередЗавершениемОбработкиПользователяИБ(ПользовательОбъект, ПараметрыОбработки);
	КонецЕсли;
	
	Если НЕ ПараметрыОбработки.Свойство("Действие") Тогда
		Возврат;
	КонецЕсли;
	
	ОбновлятьРоли = Истина;
	
	// ПриЗавершенииОбработкиПользователяИБ - поддержка модели сервиса.
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ПользователиВМоделиСервиса") Тогда
		МодульПользователиСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ПользователиСлужебныйВМоделиСервиса");
		МодульПользователиСлужебныйВМоделиСервиса.ПриЗавершенииОбработкиПользователяИБ(
			ПользовательОбъект, ПараметрыОбработки, ОбновлятьРоли);
	КонецЕсли;
	
	Если ПараметрыОбработки.Свойство("УстановкаПользователяИБ") И ОбновлятьРоли Тогда
		ПарольПользователяСервиса = Неопределено;
		Если ПользовательОбъект.ДополнительныеСвойства.Свойство("ПарольПользователяСервиса") Тогда
			ПарольПользователяСервиса = ПользовательОбъект.ДополнительныеСвойства.ПарольПользователяСервиса;
		КонецЕсли;
		
		ИнтеграцияПодсистемБСП.ПослеУстановкиПользователяИБ(ПользовательОбъект.Ссылка,
			ПарольПользователяСервиса);
	КонецЕсли;
	
	Если ПараметрыОбработки.Действие = "Записать"
	   И Пользователи.ВходВПрограммуРазрешен(ПараметрыОбработки.ОписаниеНовогоПользователяИБ) Тогда
		
		УстановитьПривилегированныйРежим(Истина);
		ОбновитьСведенияОПользователеСРазрешеннымВходом(ПользовательОбъект.Ссылка,
			Не ПараметрыОбработки.СтарыйПользовательИБСуществует
			Или Не Пользователи.ВходВПрограммуРазрешен(ПараметрыОбработки.ОписаниеСтарогоПользователяИБ));
		УстановитьПривилегированныйРежим(Ложь);
	КонецЕсли;
	
	СкопироватьНастройкиПользователяИБ(ПользовательОбъект, ПараметрыОбработки);
	
КонецПроцедуры

// Вызывается при обработке свойства СвойстваПользователяИБ пользователя в справочнике.
//
// Параметры:
//  ОписаниеПользователя   - СправочникСсылка.Пользователи
//                         - СправочникСсылка.ВнешниеПользователи
//                         - СтрокаТаблицыЗначений
//                         - Структура
//
//  ВходВПрограммуРазрешен - Булево - если указано Ложь, а сохранено Истина, тогда свойства
//                           аутентификации безусловно Ложь, т.к. были сняты в конфигураторе.
//
// Возвращаемое значение:
//  Структура
//
Функция ХранимыеСвойстваПользователяИБ(ОписаниеПользователя, ВходВПрограммуРазрешен = Ложь) Экспорт
	
	Свойства = Новый Структура;
	Свойства.Вставить("ВходВПрограммуРазрешен",       Ложь);
	Свойства.Вставить("АутентификацияСтандартная",    Ложь);
	Свойства.Вставить("АутентификацияOpenID",         Ложь);
	Свойства.Вставить("АутентификацияOpenIDConnect",  Ложь);
	Свойства.Вставить("АутентификацияТокеномДоступа", Ложь);
	Свойства.Вставить("АутентификацияОС",             Ложь);
	
	Если ТипЗнч(ОписаниеПользователя) = Тип("СправочникСсылка.Пользователи")
	 Или ТипЗнч(ОписаниеПользователя) = Тип("СправочникСсылка.ВнешниеПользователи") Тогда
		
		Запрос = Новый Запрос;
		Запрос.УстановитьПараметр("Пользователь", ОписаниеПользователя);
		Запрос.Текст =
		"ВЫБРАТЬ
		|	СведенияОПользователях.ВходВПрограммуРазрешен КАК ВходВПрограммуРазрешен,
		|	СведенияОПользователях.АутентификацияСтандартная КАК АутентификацияСтандартная,
		|	СведенияОПользователях.АутентификацияOpenID КАК АутентификацияOpenID,
		|	СведенияОПользователях.АутентификацияOpenIDConnect КАК АутентификацияOpenIDConnect,
		|	СведенияОПользователях.АутентификацияТокеномДоступа КАК АутентификацияТокеномДоступа,
		|	СведенияОПользователях.АутентификацияОС КАК АутентификацияОС
		|ИЗ
		|	РегистрСведений.СведенияОПользователях КАК СведенияОПользователях
		|ГДЕ
		|	СведенияОПользователях.Пользователь = &Пользователь";
		Выборка = Запрос.Выполнить().Выбрать();
		Если Не Выборка.Следующий() Тогда
			Возврат Свойства;
		КонецЕсли;
	Иначе
		Выборка = ОписаниеПользователя;
	КонецЕсли;
	
	СохраненныеСвойства = Новый Структура(Новый ФиксированнаяСтруктура(Свойства));
	ЗаполнитьЗначенияСвойств(СохраненныеСвойства, Выборка);
	
	Для Каждого КлючИЗначение Из Свойства Цикл
		Если ТипЗнч(СохраненныеСвойства[КлючИЗначение.Ключ]) = Тип("Булево") Тогда
			Свойства[КлючИЗначение.Ключ] = СохраненныеСвойства[КлючИЗначение.Ключ];
		КонецЕсли;
	КонецЦикла;
	
	Если Свойства.ВходВПрограммуРазрешен И Не ВходВПрограммуРазрешен Тогда
		Свойства.АутентификацияСтандартная    = Ложь;
		Свойства.АутентификацияOpenID         = Ложь;
		Свойства.АутентификацияOpenIDConnect  = Ложь;
		Свойства.АутентификацияТокеномДоступа = Ложь;
		Свойства.АутентификацияОС             = Ложь;
	КонецЕсли;
	
	Возврат Свойства;
	
КонецФункции

Процедура УстановитьХранимыеСвойстваПользователяИБ(ПользовательОбъект, ХранимыеСвойства)
	
	ПользовательОбъект.ДополнительныеСвойства.Вставить("ХранимыеСвойстваПользователяИБ", ХранимыеСвойства);
	
КонецПроцедуры

// Нельзя вызвать из фоновых заданий с пустым пользователем.
//
// Параметры:
//  ОписаниеПользователяИБ - Структура:
//                            * Роли - Массив из Строка
//                         - ПользовательИнформационнойБазы
//  Текст - Строка - возвращаемое значение текста вопроса.
//  
// Возвращаемое значение:
//  Булево
//
Функция ТребуетсяСоздатьПервогоАдминистратора(Знач ОписаниеПользователяИБ,
                                              Текст = Неопределено) Экспорт
	
	Если ОбщегоНазначения.РазделениеВключено()
		И ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		
		Возврат Ложь;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	ТекущийПользовательИБ = ПользователиИнформационнойБазы.ТекущийПользователь();
	
	Если НЕ ЗначениеЗаполнено(ТекущийПользовательИБ.Имя)
	   И ПользователиИнформационнойБазы.ПолучитьПользователей().Количество() = 0 Тогда
		
		Если ТипЗнч(ОписаниеПользователяИБ) = Тип("Структура") Тогда
			// Проверка перед записью обычного пользователя или пользователя ИБ.
			
			Если ОписаниеПользователяИБ.Свойство("Роли") Тогда
				Роли = ОписаниеПользователяИБ.Роли;
			Иначе
				Роли = Новый Массив;
			КонецЕсли;
			
			Если ЗапретРедактированияРолей()
				ИЛИ Роли.Найти("ПолныеПрава") = Неопределено
				ИЛИ Роли.Найти("АдминистраторСистемы") = Неопределено Тогда
				
				// Подготовка текста вопроса при записи первого администратора.
				Текст =
					НСтр("ru = 'В список пользователей программы добавляется первый пользователь, поэтому ему
					           |автоматически будут назначены роли ""Администратор системы"" и ""Полные права"".
					           |Продолжить?'");
				
				Если НЕ ЗапретРедактированияРолей() Тогда
					Возврат Истина;
				КонецЕсли;
				
				ИнтеграцияПодсистемБСП.ПриОпределенииТекстаВопросаПередЗаписьюПервогоАдминистратора(Текст);
				
				Возврат Истина;
			КонецЕсли;
		Иначе
			// Проверка перед записью внешнего пользователя.
			Текст =
				НСтр("ru = 'Первый пользователь информационной базы должен иметь полные права.
				           |Внешний пользователь не может быть полноправным.
				           |Сначала создайте администратора в справочнике Пользователи.'");
			Возврат Истина;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

// Проверяет наличие ролей администратора с учетом модели сервиса.
// 
// Параметры:
//  ПользовательИБ - ПользовательИнформационнойБазы - проверить указанного пользователя ИБ.
//                 - Неопределено - проверить текущего пользователя ИБ.
//  
// Возвращаемое значение:
//  Булево
//
Функция РолиАдминистратораДоступны(ПользовательИБ = Неопределено) Экспорт
	
	Если ПользовательИБ = Неопределено
	 Или ПользовательИБ = ПользователиИнформационнойБазы.ТекущийПользователь() Тогда
	
		// АПК:336-выкл Не заменять на РолиДоступны. Это специальная проверка ролей администратора.
		//@skip-check using-isinrole
		Возврат РольДоступна(Метаданные.Роли.ПолныеПрава)
		     //@skip-check using-isinrole
		     И (РольДоступна(Метаданные.Роли.АдминистраторСистемы)
		        Или ОбщегоНазначения.РазделениеВключено() );
		// АПК:336-вкл
	КонецЕсли;
	
	Возврат ПользовательИБ.Роли.Содержит(Метаданные.Роли.ПолныеПрава)
	     И (ПользовательИБ.Роли.Содержит(Метаданные.Роли.АдминистраторСистемы)
	        Или ОбщегоНазначения.РазделениеВключено() );
	
КонецФункции

// Проверяет корректность заполнения структуры описания пользователя ИБ.
// В случае нахождения ошибок устанавливает параметр Отказ в Истина и
// отправляет сообщения об ошибках.
//
// Параметры:
//  ОписаниеПользователяИБ - Структура - описание пользователя ИБ
//                 заполнение которого требуется проверить.
//
//  Отказ        - Булево - флаг отказа от выполнения операции.
//                 Устанавливается в случае ошибки.
//
//  ЭтоВнешнийПользователь - Булево - Истина если проверяется описание
//                 пользователя ИБ для внешнего пользователя.
//
// Возвращаемое значение:
//  Булево - Истина, если нет ошибок.
//
Функция ПроверитьОписаниеПользователяИБ(Знач ОписаниеПользователяИБ, Отказ, ЭтоВнешнийПользователь) Экспорт
	
	Если ОписаниеПользователяИБ.Свойство("Имя") Тогда
		Имя = ОписаниеПользователяИБ.Имя;
		
		Если ПустаяСтрока(Имя) Тогда
			// Хранилище настроек использует только первые 64 символа имени пользователя ИБ.
			ОбщегоНазначения.СообщитьПользователю(
				НСтр("ru = 'Не заполнено Имя (для входа).'"),, "Имя",,Отказ);
			
		ИначеЕсли СтрДлина(Имя) > 64 Тогда
			// Аутентификация через web использует символ ":",
			// как разделитель имени и пароля пользователя.
			ОбщегоНазначения.СообщитьПользователю(
				НСтр("ru = 'Имя (для входа) превышает 64 символа.'"),,"Имя",,Отказ);
			
		ИначеЕсли СтрНайти(Имя, ":") > 0 Тогда
			ОбщегоНазначения.СообщитьПользователю(
				НСтр("ru = 'Имя (для входа) содержит запрещенный символ "":"".'"),,"Имя",,Отказ);
		Иначе
			УстановитьПривилегированныйРежим(Истина);
			ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоИмени(Имя);
			УстановитьПривилегированныйРежим(Ложь);
			
			Если ПользовательИБ <> Неопределено
			   И ПользовательИБ.УникальныйИдентификатор
			     <> ОписаниеПользователяИБ.ИдентификаторПользователяИБ Тогда
				
				НайденныйПользователь = Неопределено;
				ПользовательПоИдентификаторуСуществует(
					ПользовательИБ.УникальныйИдентификатор, , НайденныйПользователь);
				
				Если НайденныйПользователь = Неопределено
				 ИЛИ НЕ Пользователи.ЭтоПолноправныйПользователь() Тогда
					
					ТекстОшибки = НСтр("ru = 'Имя (для входа) уже занято.'");
				Иначе
					ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Имя (для входа) уже занято для пользователя ""%1"".'"),
						Строка(НайденныйПользователь));
				КонецЕсли;
				
				ОбщегоНазначения.СообщитьПользователю(
					ТекстОшибки, , "Имя", , Отказ);
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	Если Отказ Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если Не ОписаниеПользователяИБ.Свойство("Пароль")
	 Или Не ЗначениеЗаполнено(ОписаниеПользователяИБ.Пароль) Тогда
		
		ИмяПолитикиПаролей = ИмяПолитикиПаролей(ЭтоВнешнийПользователь);
		УстановитьПривилегированныйРежим(Истина);
		ПолитикаПаролей = ПолитикиПаролейПользователей.НайтиПоИмени(ИмяПолитикиПаролей);
		Если ПолитикаПаролей = Неопределено Тогда
			МинимальнаяДлинаПаролей = ПолучитьМинимальнуюДлинуПаролейПользователей();
		Иначе
			МинимальнаяДлинаПаролей = ПолитикаПаролей.МинимальнаяДлинаПаролей;
		КонецЕсли;
		УстановитьПривилегированныйРежим(Ложь);
		Если ЗначениеЗаполнено(МинимальнаяДлинаПаролей) Тогда
			Если ЗначениеЗаполнено(ОписаниеПользователяИБ.ИдентификаторПользователяИБ) Тогда
				УстановитьПривилегированныйРежим(Истина);
				ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
					ОписаниеПользователяИБ.ИдентификаторПользователяИБ);
				УстановитьПривилегированныйРежим(Ложь);
			Иначе
				ПользовательИБ = Неопределено;
			КонецЕсли;
			
			ПарольЗадан = ОписаниеПользователяИБ.Свойство("Пароль")
				И ОписаниеПользователяИБ.Пароль <> Неопределено;
			
			АутентификацияСтандартнаяСтарая = ПользовательИБ <> Неопределено
				И ПользовательИБ.АутентификацияСтандартная;
			
			Если ОписаниеПользователяИБ.Свойство("АутентификацияСтандартная") Тогда
				АутентификацияСтандартнаяНовая = ОписаниеПользователяИБ.АутентификацияСтандартная;
			Иначе
				АутентификацияСтандартнаяНовая = АутентификацияСтандартнаяСтарая;
			КонецЕсли;
			ПроверитьПустойПароль =
				Не АутентификацияСтандартнаяСтарая
				И АутентификацияСтандартнаяНовая
				И (    ПользовательИБ = Неопределено
				     И Не ПарольЗадан
				   Или ПользовательИБ <> Неопределено
				     И Не ПользовательИБ.ПарольУстановлен);
			
			Если ПарольЗадан Или ПроверитьПустойПароль Тогда
				ОбщегоНазначения.СообщитьПользователю(
					НСтр("ru = 'Установите пароль.'"),, "СменитьПароль",, Отказ);
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	Если ОписаниеПользователяИБ.Свойство("ПользовательОС") Тогда
		
		Если Не ПустаяСтрока(ОписаниеПользователяИБ.ПользовательОС)
		   И Не СтандартныеПодсистемыСервер.ЭтоУчебнаяПлатформа() Тогда
			
			УстановитьПривилегированныйРежим(Истина);
			Попытка
				ПользовательИБ = ПользователиИнформационнойБазы.СоздатьПользователя();
				ПользовательИБ.ПользовательОС = ОписаниеПользователяИБ.ПользовательОС;
			Исключение
				ОбщегоНазначения.СообщитьПользователю(
					НСтр("ru = 'Пользователь ОС должен быть в формате
					           |""\\Имя домена\Имя пользователя"".'"),,"ПользовательОС",,Отказ);
			КонецПопытки;
			УстановитьПривилегированныйРежим(Ложь);
		КонецЕсли;
		
	КонецЕсли;
	
	Если ОписаниеПользователяИБ.Свойство("АдресЭлектроннойПочты")
		И ОписаниеПользователяИБ.Свойство("ЗапрещеноВосстанавливатьПароль") Тогда
		
		Если Не ОписаниеПользователяИБ.ЗапрещеноВосстанавливатьПароль
		   И ПустаяСтрока(ОписаниеПользователяИБ.АдресЭлектроннойПочты) Тогда
				
				ОбщегоНазначения.СообщитьПользователю(
					НСтр("ru = 'Не заполнена электронная почта для восстановления пароля.'"),,
					"КонтактнаяИнформация",,Отказ);
		КонецЕсли;
		
	КонецЕсли;
	
	Возврат НЕ Отказ;
	
КонецФункции

// Обновляет составы пользователей у групп пользователей с учетом иерархии
// в регистре сведений "Составы групп пользователей".
//  Данные регистра используются в форме списка и формы выбора пользователей.
//  Данные регистра могут быть использованы для повышения производительности запросов,
// т.к. не требуется работать с иерархией.
//
// Параметры:
//  ГруппаПользователей - СправочникСсылка.ГруппыПользователей.
//
//  Пользователь - Неопределено                            - для всех пользователей.
//               - Массив из СправочникСсылка.Пользователи - для указанных пользователей.
//               - СправочникСсылка.Пользователи           - для указанного пользователя.
//
//  УчастникиИзменений - Неопределено - никаких действий.
//                     - Соответствие из КлючИЗначение:
//                         * Ключ - СправочникСсылка.Пользователи - заполняет соответствие
//                                  пользователями для которых есть изменения.
//                         * Значение - Неопределено
//
//  ИзмененныеГруппы   - Неопределено - никаких действий.
//                     - Соответствие из КлючИЗначение:
//                         * Ключ - СправочникСсылка.ГруппыПользователей - заполняет соответствие
//                                  группами пользователей для которых есть изменения.
//                         * Значение - Неопределено
//
Процедура ОбновитьСоставыГруппПользователей(Знач ГруппаПользователей,
                                            Знач Пользователь       = Неопределено,
                                            Знач УчастникиИзменений = Неопределено,
                                            Знач ИзмененныеГруппы   = Неопределено) Экспорт
	
	Если НЕ ЗначениеЗаполнено(ГруппаПользователей) Тогда
		Возврат;
	КонецЕсли;
	
	Если ТипЗнч(Пользователь) = Тип("Массив") И Пользователь.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Если УчастникиИзменений = Неопределено Тогда
		ТекущиеУчастникиИзменений = Новый Соответствие;
	Иначе
		ТекущиеУчастникиИзменений = УчастникиИзменений;
	КонецЕсли;
	
	Если ИзмененныеГруппы = Неопределено Тогда
		ТекущиеИзмененныеГруппы = Новый Соответствие;
	Иначе
		ТекущиеИзмененныеГруппы = ИзмененныеГруппы;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	НачатьТранзакцию();
	Попытка
		Если ГруппаПользователей = Справочники.ГруппыПользователей.ВсеПользователи Тогда
			
			ОбновитьСоставГруппыВсеПользователи(
				Пользователь, , ТекущиеУчастникиИзменений, ТекущиеИзмененныеГруппы);
		Иначе
			ОбновитьСоставыИерархическихГруппПользователей(
				ГруппаПользователей,
				Пользователь,
				ТекущиеУчастникиИзменений,
				ТекущиеИзмененныеГруппы);
		КонецЕсли;
		
		Если УчастникиИзменений = Неопределено
		   И ИзмененныеГруппы   = Неопределено Тогда
			
			ПослеОбновленияСоставовГруппПользователей(
				ТекущиеУчастникиИзменений, ТекущиеИзмененныеГруппы);
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Обновление ресурс Используется при изменении реквизитов ПометкаУдаления, Недействителен.
//
// Параметры:
//  ПользовательИлиГруппа - СправочникСсылка.Пользователи,
//                        - СправочникСсылка.ВнешниеПользователи,
//                        - СправочникСсылка.ГруппыПользователей,
//                        - СправочникСсылка.ГруппыВнешнихПользователей.
//
//  УчастникиИзменений - Неопределено - никаких действий.
//                     - Соответствие из КлючИЗначение:
//                         * Ключ - СправочникСсылка.Пользователи
//                                - СправочникСсылка.ВнешниеПользователи - заполняет соответствие
//                                  пользователями для которых есть изменения.
//                         * Значение - Неопределено
//
//  ИзмененныеГруппы   - Неопределено - никаких действий.
//                     - Соответствие из КлючИЗначение:
//                         * Ключ - СправочникСсылка.ГруппыПользователей
//                                - СправочникСсылка.ГруппыВнешнихПользователей - заполняет соответствие
//                                  группами пользователей для которых есть изменения.
//                         * Значение - Неопределено
//
Процедура ОбновитьИспользуемостьСоставовГруппПользователей(Знач ПользовательИлиГруппа,
                                                           Знач УчастникиИзменений,
                                                           Знач ИзмененныеГруппы) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ПользовательИлиГруппа", ПользовательИлиГруппа);
	// АПК:1377-выкл - №654.2.1 Использование 5 типов "через точку" необходимо
	// и допустимо, так как объем данных небольшой (от единиц до тысяч).
	Запрос.Текст =
	"ВЫБРАТЬ
	|	СоставыГруппПользователей.ГруппаПользователей,
	|	СоставыГруппПользователей.Пользователь,
	|	ВЫБОР
	|		КОГДА СоставыГруппПользователей.ГруппаПользователей.ПометкаУдаления
	|			ТОГДА ЛОЖЬ
	|		КОГДА СоставыГруппПользователей.Пользователь.ПометкаУдаления
	|			ТОГДА ЛОЖЬ
	|		КОГДА СоставыГруппПользователей.Пользователь.Недействителен
	|			ТОГДА ЛОЖЬ
	|		ИНАЧЕ ИСТИНА
	|	КОНЕЦ КАК Используется
	|ИЗ
	|	РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|ГДЕ
	|	&Отбор
	|	И ВЫБОР
	|			КОГДА СоставыГруппПользователей.ГруппаПользователей.ПометкаУдаления
	|				ТОГДА ЛОЖЬ
	|			КОГДА СоставыГруппПользователей.Пользователь.ПометкаУдаления
	|				ТОГДА ЛОЖЬ
	|			КОГДА СоставыГруппПользователей.Пользователь.Недействителен
	|				ТОГДА ЛОЖЬ
	|			ИНАЧЕ ИСТИНА
	|		КОНЕЦ <> СоставыГруппПользователей.Используется";
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.СоставыГруппПользователей");
	// АПК:1377-вкл.
	
	Если ТипЗнч(ПользовательИлиГруппа) = Тип("СправочникСсылка.Пользователи")
	 ИЛИ ТипЗнч(ПользовательИлиГруппа) = Тип("СправочникСсылка.ВнешниеПользователи") Тогда
		
		ЭлементБлокировки.УстановитьЗначение("Пользователь", ПользовательИлиГруппа);
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&Отбор",
			"СоставыГруппПользователей.Пользователь = &ПользовательИлиГруппа");
	Иначе
		ЭлементБлокировки.УстановитьЗначение("ГруппаПользователей", ПользовательИлиГруппа);
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&Отбор",
			"СоставыГруппПользователей.ГруппаПользователей = &ПользовательИлиГруппа");
	КонецЕсли;
	
	НаборЗаписи = РегистрыСведений.СоставыГруппПользователей.СоздатьНаборЗаписей();
	Запись = НаборЗаписи.Добавить();
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		Выборка = Запрос.Выполнить().Выбрать();
		Пока Выборка.Следующий() Цикл
			
			НаборЗаписи.Отбор.ГруппаПользователей.Установить(Выборка.ГруппаПользователей);
			НаборЗаписи.Отбор.Пользователь.Установить(Выборка.Пользователь);
			
			Запись.ГруппаПользователей = Выборка.ГруппаПользователей;
			Запись.Пользователь        = Выборка.Пользователь;
			Запись.Используется        = Выборка.Используется;
			
			НаборЗаписи.Записать();
			
			ИзмененныеГруппы.Вставить(Выборка.ГруппаПользователей);
			УчастникиИзменений.Вставить(Выборка.Пользователь);
		КонецЦикла;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Только для внутреннего использования.
Процедура ПослеОбновленияСоставовГруппПользователей(УчастникиИзменений, ИзмененныеГруппы) Экспорт
	
	Если УчастникиИзменений.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	УчастникиИзмененийМассив = Новый Массив;
	
	Для каждого КлючИЗначение Из УчастникиИзменений Цикл
		УчастникиИзмененийМассив.Добавить(КлючИЗначение.Ключ);
	КонецЦикла;
	
	ИзмененныеГруппыМассив = Новый Массив;
	Для каждого КлючИЗначение Из ИзмененныеГруппы Цикл
		ИзмененныеГруппыМассив.Добавить(КлючИЗначение.Ключ);
	КонецЦикла;
	
	ИнтеграцияПодсистемБСП.ПослеОбновленияСоставовГруппПользователей(УчастникиИзмененийМассив,
		ИзмененныеГруппыМассив);
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции работы с внешними пользователями.

// Обновляет составы внешних пользователей у групп внешних пользователей
// с учетом иерархии в регистре сведений "Составы групп пользователей".
//  Данные используются в форме списка и форме выбора внешних пользователей.
//  Данные могут быть использованы в целях для повышения производительности,
// так как не требуется работать с иерархией на языке запросов.
//
// Параметры:
//  ГруппаВнешнихПользователей - СправочникСсылка.ГруппыВнешнихПользователей
//                        Когда указана группа ВсеВнешниеПользователи обновляются также все
//                        автоматические группы внешних пользователей по типам объектов авторизации.
//
//  ВнешнийПользователь - Неопределено - для всех внешних пользователей.
//                      - Массив из СправочникСсылка.ВнешниеПользователи - для указанных
//                          внешних пользователей.
//                      - СправочникСсылка.ВнешниеПользователи - для указанного внешнего пользователя.
//
//  УчастникиИзменений - Неопределено - никаких действий.
//                     - Соответствие из КлючИЗначение:
//                         * Ключ - СправочникСсылка.ВнешниеПользователи - заполняет соответствие
//                                  внешними пользователями для которых есть изменения.
//                         * Значение - Неопределено
//
//  ИзмененныеГруппы   - Неопределено - никаких действий.
//                     - Соответствие из КлючИЗначение:
//                         * Ключ - СправочникСсылка.ГруппыВнешнихПользователей - заполняет соответствие
//                                  группами внешних пользователей для которых есть изменения.
//                         * Значение - Неопределено
//
Процедура ОбновитьСоставыГруппВнешнихПользователей(Знач ГруппаВнешнихПользователей,
                                                   Знач ВнешнийПользователь = Неопределено,
                                                   Знач УчастникиИзменений  = Неопределено,
                                                   Знач ИзмененныеГруппы    = Неопределено) Экспорт
	
	Если НЕ ЗначениеЗаполнено(ГруппаВнешнихПользователей) Тогда
		Возврат;
	КонецЕсли;
	
	Если ТипЗнч(ВнешнийПользователь) = Тип("Массив") И ВнешнийПользователь.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Если УчастникиИзменений = Неопределено Тогда
		ТекущиеУчастникиИзменений = Новый Соответствие;
	Иначе
		ТекущиеУчастникиИзменений = УчастникиИзменений;
	КонецЕсли;
	
	Если ИзмененныеГруппы = Неопределено Тогда
		ТекущиеИзмененныеГруппы = Новый Соответствие;
	Иначе
		ТекущиеИзмененныеГруппы = ИзмененныеГруппы;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	НачатьТранзакцию();
	Попытка
		Если ГруппаВнешнихПользователей = Справочники.ГруппыВнешнихПользователей.ВсеВнешниеПользователи Тогда
			
			ОбновитьСоставГруппыВсеПользователи(
				ВнешнийПользователь, Истина, ТекущиеУчастникиИзменений, ТекущиеИзмененныеГруппы);
			
			ОбновитьСоставыГруппПоТипамОбъектовАвторизации(
				Неопределено, ВнешнийПользователь, ТекущиеУчастникиИзменений, ТекущиеИзмененныеГруппы);
			
		Иначе
			ВсеОбъектыАвторизации = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ГруппаВнешнихПользователей,
				"ВсеОбъектыАвторизации");
			ВсеОбъектыАвторизации = ?(ВсеОбъектыАвторизации = Неопределено, Ложь, ВсеОбъектыАвторизации);
			
			Если ВсеОбъектыАвторизации Тогда
				ОбновитьСоставыГруппПоТипамОбъектовАвторизации(
					ГруппаВнешнихПользователей,
					ВнешнийПользователь,
					ТекущиеУчастникиИзменений,
					ТекущиеИзмененныеГруппы);
			Иначе
				ОбновитьСоставыИерархическихГруппПользователей(
					ГруппаВнешнихПользователей,
					ВнешнийПользователь,
					ТекущиеУчастникиИзменений,
					ТекущиеИзмененныеГруппы);
			КонецЕсли;
		КонецЕсли;
		
		Если УчастникиИзменений = Неопределено
		   И ИзмененныеГруппы   = Неопределено Тогда
			
			ПослеОбновленияСоставовГруппВнешнихПользователей(
				ТекущиеУчастникиИзменений, ТекущиеИзмененныеГруппы);
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Только для внутреннего использования.
Процедура ПослеОбновленияСоставовГруппВнешнихПользователей(УчастникиИзменений, ИзмененныеГруппы) Экспорт
	
	Если УчастникиИзменений.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	УчастникиИзмененийМассив = Новый Массив;
	Для каждого КлючИЗначение Из УчастникиИзменений Цикл
		УчастникиИзмененийМассив.Добавить(КлючИЗначение.Ключ);
	КонецЦикла;
	
	ОбновитьРолиВнешнихПользователей(УчастникиИзмененийМассив);
	
	ИзмененныеГруппыМассив = Новый Массив;
	Для каждого КлючИЗначение Из ИзмененныеГруппы Цикл
		ИзмененныеГруппыМассив.Добавить(КлючИЗначение.Ключ);
	КонецЦикла;
	
	ИнтеграцияПодсистемБСП.ПослеОбновленияСоставовГруппПользователей(УчастникиИзмененийМассив,
		ИзмененныеГруппыМассив);
	
КонецПроцедуры

// Обновляет список ролей пользователей информационной базы, которые соответствуют
// внешним пользователям. Состав ролей вычисляется исходя из вхождения внешних
// пользователей в группы внешних пользователей, кроме тех внешних пользователей,
// для которых роли установлены непосредственно.
//  Требуется только при разрешении редактирования ролей, например, если
// встроена подсистема Управление доступом, то эта процедура не требуется.
// 
// Параметры:
//  МассивВнешнихПользователей - Неопределено - все внешние пользователи.
//                               СправочникСсылка.ГруппаВнешнихПользователей,
//                               Массив элементов СправочникСсылка.ВнешниеПользователи.
//
Процедура ОбновитьРолиВнешнихПользователей(Знач МассивВнешнихПользователей = Неопределено) Экспорт
	
	Если ЗапретРедактированияРолей() Тогда
		// Роли устанавливаются другим механизмом, например, механизмом подсистемы УправлениеДоступом.
		Возврат;
	КонецЕсли;
	
	Если ТипЗнч(МассивВнешнихПользователей) = Тип("Массив")
	   И МассивВнешнихПользователей.Количество() = 0 Тогда
		
		Возврат;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	НачатьТранзакцию();
	Попытка
		Если ТипЗнч(МассивВнешнихПользователей) <> Тип("Массив") Тогда
			
			Если МассивВнешнихПользователей = Неопределено Тогда
				ГруппаВнешнихПользователей = Справочники.ГруппыВнешнихПользователей.ВсеВнешниеПользователи;
			Иначе
				ГруппаВнешнихПользователей = МассивВнешнихПользователей;
			КонецЕсли;
			
			Запрос = Новый Запрос;
			Запрос.УстановитьПараметр("ГруппаВнешнихПользователей", ГруппаВнешнихПользователей);
			Запрос.Текст =
			"ВЫБРАТЬ
			|	СоставыГруппПользователей.Пользователь
			|ИЗ
			|	РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
			|ГДЕ
			|	СоставыГруппПользователей.ГруппаПользователей = &ГруппаВнешнихПользователей";
			
			МассивВнешнихПользователей = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Пользователь");
		КонецЕсли;
		
		Пользователи.НайтиНеоднозначныхПользователейИБ(Неопределено);
		
		ИдентификаторыПользователейИБ = Новый Соответствие;
		
		Запрос = Новый Запрос;
		Запрос.УстановитьПараметр("ВнешниеПользователи", МассивВнешнихПользователей);
		Запрос.Текст =
		"ВЫБРАТЬ
		|	ВнешниеПользователи.Ссылка КАК ВнешнийПользователь,
		|	ВнешниеПользователи.ИдентификаторПользователяИБ
		|ИЗ
		|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
		|ГДЕ
		|	ВнешниеПользователи.Ссылка В(&ВнешниеПользователи)
		|	И (НЕ ВнешниеПользователи.УстановитьРолиНепосредственно)";
		
		Выборка = Запрос.Выполнить().Выбрать();
		Пока Выборка.Следующий() Цикл
			ИдентификаторыПользователейИБ.Вставить(
				Выборка.ВнешнийПользователь, Выборка.ИдентификаторПользователяИБ);
		КонецЦикла;
		
		// Подготовка таблицы старых ролей внешних пользователей.
		СтарыеРолиВнешнихПользователей = Новый ТаблицаЗначений;
		
		СтарыеРолиВнешнихПользователей.Колонки.Добавить(
			"ВнешнийПользователь", Новый ОписаниеТипов("СправочникСсылка.ВнешниеПользователи"));
		
		СтарыеРолиВнешнихПользователей.Колонки.Добавить(
			"Роль", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(200)));
		
		ТекущийНомер = МассивВнешнихПользователей.Количество() - 1;
		Пока ТекущийНомер >= 0 Цикл
			
			// Проверка необходимости обработки пользователя.
			ПользовательИБ = Неопределено;
			ИдентификаторПользователяИБ = ИдентификаторыПользователейИБ[МассивВнешнихПользователей[ТекущийНомер]];
			Если ИдентификаторПользователяИБ <> Неопределено Тогда
				
				ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
					ИдентификаторПользователяИБ);
			КонецЕсли;
			
			Если ПользовательИБ = Неопределено
			 ИЛИ ПустаяСтрока(ПользовательИБ.Имя) Тогда
				
				МассивВнешнихПользователей.Удалить(ТекущийНомер);
			Иначе
				Для каждого Роль Из ПользовательИБ.Роли Цикл
					СтараяРольВнешнегоПользователя = СтарыеРолиВнешнихПользователей.Добавить();
					СтараяРольВнешнегоПользователя.ВнешнийПользователь = МассивВнешнихПользователей[ТекущийНомер];
					СтараяРольВнешнегоПользователя.Роль = Роль.Имя;
				КонецЦикла;
			КонецЕсли;
			ТекущийНомер = ТекущийНомер - 1;
		КонецЦикла;
		
		// Подготовка списка ролей отсутствующих в метаданных и которые нужно переустановить.
		Запрос = Новый Запрос;
		Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
		Запрос.УстановитьПараметр("ВнешниеПользователи", МассивВнешнихПользователей);
		Запрос.УстановитьПараметр("ВсеРоли", ВсеРоли().Таблица.Получить());
		Запрос.УстановитьПараметр("СтарыеРолиВнешнихПользователей", СтарыеРолиВнешнихПользователей);
		Запрос.УстановитьПараметр("ИспользоватьВнешнихПользователей",
			ПолучитьФункциональнуюОпцию("ИспользоватьВнешнихПользователей"));
		// АПК:96-выкл - №434 Использование ОБЪЕДИНИТЬ допустимо, так как
		// строки не должны повторятся и объем данных небольшой (от единиц до тысяч).
		Запрос.Текст =
		"ВЫБРАТЬ
		|	СтарыеРолиВнешнихПользователей.ВнешнийПользователь,
		|	СтарыеРолиВнешнихПользователей.Роль
		|ПОМЕСТИТЬ СтарыеРолиВнешнихПользователей
		|ИЗ
		|	&СтарыеРолиВнешнихПользователей КАК СтарыеРолиВнешнихПользователей
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ВсеРоли.Имя
		|ПОМЕСТИТЬ ВсеРоли
		|ИЗ
		|	&ВсеРоли КАК ВсеРоли
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	СоставыГруппПользователей.ГруппаПользователей КАК ГруппаВнешнихПользователей,
		|	СоставыГруппПользователей.Пользователь КАК ВнешнийПользователь,
		|	Роли.Роль.Имя КАК Роль
		|ПОМЕСТИТЬ ВсеНовыеРолиВнешнихПользователей
		|ИЗ
		|	Справочник.ГруппыВнешнихПользователей.Роли КАК Роли
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
		|		ПО (СоставыГруппПользователей.Пользователь В (&ВнешниеПользователи))
		|			И (СоставыГруппПользователей.ГруппаПользователей = Роли.Ссылка)
		|			И (&ИспользоватьВнешнихПользователей = ИСТИНА)
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ВсеНовыеРолиВнешнихПользователей.ВнешнийПользователь,
		|	ВсеНовыеРолиВнешнихПользователей.Роль
		|ПОМЕСТИТЬ НовыеРолиВнешнихПользователей
		|ИЗ
		|	ВсеНовыеРолиВнешнихПользователей КАК ВсеНовыеРолиВнешнихПользователей
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	СтарыеРолиВнешнихПользователей.ВнешнийПользователь
		|ПОМЕСТИТЬ ИзмененныеВнешниеПользователи
		|ИЗ
		|	СтарыеРолиВнешнихПользователей КАК СтарыеРолиВнешнихПользователей
		|		ЛЕВОЕ СОЕДИНЕНИЕ НовыеРолиВнешнихПользователей КАК НовыеРолиВнешнихПользователей
		|		ПО (НовыеРолиВнешнихПользователей.ВнешнийПользователь = СтарыеРолиВнешнихПользователей.ВнешнийПользователь)
		|			И (НовыеРолиВнешнихПользователей.Роль = СтарыеРолиВнешнихПользователей.Роль)
		|ГДЕ
		|	НовыеРолиВнешнихПользователей.Роль ЕСТЬ NULL 
		|
		|ОБЪЕДИНИТЬ
		|
		|ВЫБРАТЬ
		|	НовыеРолиВнешнихПользователей.ВнешнийПользователь
		|ИЗ
		|	НовыеРолиВнешнихПользователей КАК НовыеРолиВнешнихПользователей
		|		ЛЕВОЕ СОЕДИНЕНИЕ СтарыеРолиВнешнихПользователей КАК СтарыеРолиВнешнихПользователей
		|		ПО НовыеРолиВнешнихПользователей.ВнешнийПользователь = СтарыеРолиВнешнихПользователей.ВнешнийПользователь
		|			И НовыеРолиВнешнихПользователей.Роль = СтарыеРолиВнешнихПользователей.Роль
		|ГДЕ
		|	СтарыеРолиВнешнихПользователей.Роль ЕСТЬ NULL 
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ВсеНовыеРолиВнешнихПользователей.ГруппаВнешнихПользователей,
		|	ВсеНовыеРолиВнешнихПользователей.ВнешнийПользователь,
		|	ВсеНовыеРолиВнешнихПользователей.Роль
		|ИЗ
		|	ВсеНовыеРолиВнешнихПользователей КАК ВсеНовыеРолиВнешнихПользователей
		|ГДЕ
		|	НЕ ИСТИНА В
		|				(ВЫБРАТЬ ПЕРВЫЕ 1
		|					ИСТИНА КАК ЗначениеИстина
		|				ИЗ
		|					ВсеРоли КАК ВсеРоли
		|				ГДЕ
		|					ВсеРоли.Имя = ВсеНовыеРолиВнешнихПользователей.Роль)";
		// АПК:96-вкл.
		
		// Регистрация ошибок имен ролей в профилях групп доступа.
		Выборка = Запрос.Выполнить().Выбрать();
		Пока Выборка.Следующий() Цикл
			ВнешнийПользователь = Выборка.ВнешнийПользователь; // СправочникСсылка.ВнешниеПользователи
			
			ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'При обновлении ролей внешнего пользователя
				          |""%1""
				          |не существует роль ""%2""
				          |группы внешних пользователей ""%3"".'"),
				СокрЛП(ВнешнийПользователь),
				Выборка.Роль,
				Строка(Выборка.ГруппаВнешнихПользователей));
			
			ЗаписьЖурналаРегистрации(
				НСтр("ru = 'Пользователи.Роль не найдена в метаданных'",
				     ОбщегоНазначения.КодОсновногоЯзыка()),
				УровеньЖурналаРегистрации.Ошибка,
				,
				,
				ТекстСообщения,
				РежимТранзакцииЗаписиЖурналаРегистрации.Транзакционная);
		КонецЦикла;
		
		// Обновление ролей пользователей ИБ.
		Запрос.Текст =
		"ВЫБРАТЬ
		|	ИзмененныеВнешниеПользователиИРоли.ВнешнийПользователь,
		|	ИзмененныеВнешниеПользователиИРоли.Роль
		|ИЗ
		|	(ВЫБРАТЬ
		|		НовыеРолиВнешнихПользователей.ВнешнийПользователь КАК ВнешнийПользователь,
		|		НовыеРолиВнешнихПользователей.Роль КАК Роль
		|	ИЗ
		|		НовыеРолиВнешнихПользователей КАК НовыеРолиВнешнихПользователей
		|	ГДЕ
		|		НовыеРолиВнешнихПользователей.ВнешнийПользователь В
		|				(ВЫБРАТЬ
		|					ИзмененныеВнешниеПользователи.ВнешнийПользователь
		|				ИЗ
		|					ИзмененныеВнешниеПользователи КАК ИзмененныеВнешниеПользователи)
		|	
		|	ОБЪЕДИНИТЬ ВСЕ
		|	
		|	ВЫБРАТЬ
		|		ВнешниеПользователи.Ссылка,
		|		""""
		|	ИЗ
		|		Справочник.ВнешниеПользователи КАК ВнешниеПользователи
		|	ГДЕ
		|		ВнешниеПользователи.Ссылка В
		|				(ВЫБРАТЬ
		|					ИзмененныеВнешниеПользователи.ВнешнийПользователь
		|				ИЗ
		|					ИзмененныеВнешниеПользователи КАК ИзмененныеВнешниеПользователи)) КАК ИзмененныеВнешниеПользователиИРоли
		|
		|УПОРЯДОЧИТЬ ПО
		|	ИзмененныеВнешниеПользователиИРоли.ВнешнийПользователь,
		|	ИзмененныеВнешниеПользователиИРоли.Роль";
		Выборка = Запрос.Выполнить().Выбрать();
		
		ПользовательИБ = Неопределено;
		Пока Выборка.Следующий() Цикл
			Если ЗначениеЗаполнено(Выборка.Роль) Тогда
				ПользовательИБ.Роли.Добавить(Метаданные.Роли[Выборка.Роль]);
				Продолжить;
			КонецЕсли;
			Если ПользовательИБ <> Неопределено Тогда
				ПользовательИБ.Записать();
			КонецЕсли;
			
			ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
				ИдентификаторыПользователейИБ[Выборка.ВнешнийПользователь]);
			
			ПользовательИБ.Роли.Очистить();
		КонецЦикла;
		Если ПользовательИБ <> Неопределено Тогда
			ПользовательИБ.Записать();
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Проверяет, что объект информационной базы используется в качестве объекта авторизации
// какого-нибудь внешнего пользователя, кроме заданного внешнего пользователя (если задан).
//
// Параметры:
//  СсылкаНаОбъектАвторизации - ОпределяемыйТип.ВнешнийПользователь
//  СсылкаНаТекущегоВнешнегоПользователя - СправочникСсылка.ВнешниеПользователи
//  НайденныйВнешнийПользователь - Неопределено
//                               - СправочникСсылка.ВнешниеПользователи
//  ЕстьПравоДобавленияВнешнегоПользователя - Булево
//  ТекстОшибки - Строка
//
// Возвращаемое значение:
//  Булево
//
Функция ОбъектАвторизацииИспользуется(Знач СсылкаНаОбъектАвторизации,
                                      Знач СсылкаНаТекущегоВнешнегоПользователя,
                                      НайденныйВнешнийПользователь = Неопределено,
                                      ЕстьПравоДобавленияВнешнегоПользователя = Ложь,
                                      ТекстОшибки = "") Экспорт
	
	ЕстьПравоДобавленияВнешнегоПользователя = ПравоДоступа(
		"Добавление", Метаданные.Справочники.ВнешниеПользователи);
	
	УстановитьПривилегированныйРежим(Истина);
	
	НачатьТранзакцию();
	Попытка
		СвойстваОбъектаАвторизации = СвойстваОбъектаАвторизации(СсылкаНаОбъектАвторизации,
			СсылкаНаТекущегоВнешнегоПользователя);
		
		Если СвойстваОбъектаАвторизации.Используется Тогда
			НайденныйВнешнийПользователь = СвойстваОбъектаАвторизации.Ссылка;
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Если СвойстваОбъектаАвторизации.Используется Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Уже существует внешний пользователь, связанный с объектом ""%1"".'"),
			СсылкаНаОбъектАвторизации);
	КонецЕсли;
	
	Возврат СвойстваОбъектаАвторизации.Используется;
	
КонецФункции

// Возвращает ссылку на внешнего пользователя.
//
// Параметры:
//  СсылкаНаОбъектАвторизации - ОпределяемыйТип.ВнешнийПользователь
//  СсылкаНаТекущегоВнешнегоПользователя - СправочникСсылка.ВнешниеПользователи
//
// Возвращаемое значение:
//  Структура:
//    * Используется - Булево
//    * Ссылка - СправочникСсылка.ВнешниеПользователи
//
Функция СвойстваОбъектаАвторизации(СсылкаНаОбъектАвторизации, СсылкаНаТекущегоВнешнегоПользователя)
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ВнешниеПользователи.Ссылка
	|ИЗ
	|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
	|ГДЕ
	|	ВнешниеПользователи.ОбъектАвторизации = &СсылкаНаОбъектАвторизации
	|	И ВнешниеПользователи.Ссылка <> &СсылкаНаТекущегоВнешнегоПользователя");
	
	Запрос.УстановитьПараметр("СсылкаНаТекущегоВнешнегоПользователя", СсылкаНаТекущегоВнешнегоПользователя);
	Запрос.УстановитьПараметр("СсылкаНаОбъектАвторизации", СсылкаНаОбъектАвторизации);
	
	Выборка = Запрос.Выполнить().Выбрать();
	
	СвойстваОбъектаАвторизации = Новый Структура;
	СвойстваОбъектаАвторизации.Вставить("Используется", Выборка.Следующий());
	СвойстваОбъектаАвторизации.Вставить("Ссылка", Выборка.Ссылка);
	
	Возврат СвойстваОбъектаАвторизации;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Работа с настройками пользователя информационной базы.

// Копирует настройки от пользователя источника пользователю приемнику. При значении
// параметра Переносить = Истина, настройки у пользователя источника удаляются.
//
// Параметры:
//   ИмяПользователяИсточник - Строка - имя пользователя ИБ от которого будет производиться копирование настроек.
//
// ИмяПользователяПриемник - Строка - Имя пользователя ИБ которому будут записаны настройки.
//
// Переносить              - Булево - Если Истина - настройки переносятся от одного пользователя другому,
//                           если Ложь - копируются новому.
//
Процедура СкопироватьНастройкиПользователя(ИмяПользователяИсточник, ИмяПользователяПриемник, Переносить = Ложь) Экспорт
	
	// Перенос пользовательских настроек отчетов.
	СкопироватьНастройки(ХранилищеПользовательскихНастроекОтчетов, ИмяПользователяИсточник, ИмяПользователяПриемник, Переносить);
	// Перенос настроек внешнего вида.
	СкопироватьНастройки(ХранилищеСистемныхНастроек,ИмяПользователяИсточник, ИмяПользователяПриемник, Переносить);
	// Перенос произвольных настроек пользователей.
	СкопироватьНастройки(ХранилищеОбщихНастроек, ИмяПользователяИсточник, ИмяПользователяПриемник, Переносить);
	// Перенос настроек данных форм.
	СкопироватьНастройки(ХранилищеНастроекДанныхФорм, ИмяПользователяИсточник, ИмяПользователяПриемник, Переносить);
	// Перенос настроек быстрого доступа дополнительных отчетов и обработок.
	Если Не Переносить Тогда
		СкопироватьПрочиеПользовательскиеНастройки(ИмяПользователяИсточник, ИмяПользователяПриемник);
	КонецЕсли;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции перемещения пользователей между группами.

// Перемещает пользователя из одной группы в другую.
//
// Параметры:
//  МассивПользователей - Массив - пользователи, которых нужно перенести в новую группу.
//  ГруппаИсточник      - СправочникСсылка.ГруппыПользователей - группа, из которой происходит
//                        перенос пользователей.
//  ГруппаПриемник      - СправочникСсылка.ГруппыПользователей - группа, в которую переносятся
//                        пользователи.
//  Перемещение         - Булево - если Истина, то пользователь удаляется из старой группы.
//
// Возвращаемое значение:
//  Строка - сообщение о результате переноса.
//
Функция ПеремещениеПользователяВНовуюГруппу(МассивПользователей, ГруппаИсточник,
												ГруппаПриемник, Перемещение) Экспорт
	
	Если ГруппаПриемник = Неопределено
		Или ГруппаПриемник = ГруппаИсточник Тогда
		Возврат Неопределено;
	КонецЕсли;
	МассивПеремещенныхПользователей = Новый Массив;
	МассивНеПеремещенныхПользователей = Новый Массив;
	
	Для Каждого ПользовательСсылка Из МассивПользователей Цикл
		
		Если ТипЗнч(ПользовательСсылка) <> Тип("СправочникСсылка.Пользователи")
			И ТипЗнч(ПользовательСсылка) <> Тип("СправочникСсылка.ВнешниеПользователи") Тогда
			Продолжить;
		КонецЕсли;
		
		Если Не ВозможноПеремещениеПользователя(ГруппаПриемник, ПользовательСсылка) Тогда
			МассивНеПеремещенныхПользователей.Добавить(ПользовательСсылка);
			Продолжить;
		КонецЕсли;
		
		Если ТипЗнч(ПользовательСсылка) = Тип("СправочникСсылка.Пользователи") Тогда
			ИмяКолонкиСостава = "Пользователь";
		Иначе
			ИмяКолонкиСостава = "ВнешнийПользователь";
		КонецЕсли;
		
		// Если переносимый пользователь не входит в новую группу, то переносим.
		Если ГруппаПриемник = Справочники.ГруппыПользователей.ВсеПользователи
			Или ГруппаПриемник = Справочники.ГруппыВнешнихПользователей.ВсеВнешниеПользователи Тогда
			
			Если Перемещение Тогда
				Удален = Ложь;
				УдалитьПользователяИзГруппы(ГруппаИсточник, ПользовательСсылка, ИмяКолонкиСостава, Удален);
			Иначе
				Удален = Истина;
			КонецЕсли;
			Если Удален Тогда
				МассивПеремещенныхПользователей.Добавить(ПользовательСсылка);
			КонецЕсли;
		Иначе
			Добавлен = Ложь;
			ДобавитьПользователяВГруппу(ГруппаПриемник, ПользовательСсылка, ИмяКолонкиСостава, Добавлен);
			Если Добавлен Тогда
				// Удаление пользователя из старой группы.
				Если Перемещение Тогда
					УдалитьПользователяИзГруппы(ГруппаИсточник, ПользовательСсылка, ИмяКолонкиСостава);
				КонецЕсли;
				МассивПеремещенныхПользователей.Добавить(ПользовательСсылка);
			КонецЕсли;
		КонецЕсли;
		
	КонецЦикла;
	
	СообщениеПользователю = ФормированиеСообщенияПользователю(
		МассивПеремещенныхПользователей, ГруппаПриемник, Перемещение, МассивНеПеремещенныхПользователей, ГруппаИсточник);
	
	Если МассивПеремещенныхПользователей.Количество() = 0 И МассивНеПеремещенныхПользователей.Количество() = 0 Тогда
		Если МассивПользователей.Количество() = 1 Тогда
			ТекстСообщения = НСтр("ru = 'Пользователь ""%1"" уже включен в группу ""%2"".'");
			ИмяПеремещаемогоПользователя = Строка(МассивПользователей[0]);
		Иначе
			ТекстСообщения = НСтр("ru = 'Все выбранные пользователи уже включены в группу ""%2"".'");
			ИмяПеремещаемогоПользователя = "";
		КонецЕсли;
		НаименованиеГруппы = Строка(ГруппаПриемник);
		СообщениеПользователю.Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстСообщения,
			ИмяПеремещаемогоПользователя, НаименованиеГруппы);
		СообщениеПользователю.ЕстьОшибки = Истина;
		Возврат СообщениеПользователю;
	КонецЕсли;
	
	Возврат СообщениеПользователю;
	
КонецФункции

// Проверяет возможность включения внешнего пользователя в группу.
//
// Параметры:
//  ГруппаПриемник     - СправочникСсылка.ГруппыПользователей
//                     - СправочникСсылка.ГруппыВнешнихПользователей - группа,
//                          в которую добавляется пользователь.
//
//  ПользовательСсылка - СправочникСсылка.Пользователи
//                     - СправочникСсылка.ВнешниеПользователи - пользователь,
//                         которого необходимо добавить в группу.
//
// Возвращаемое значение:
//  Булево - Ложь, если добавление пользователя в группу невозможно.
//
Функция ВозможноПеремещениеПользователя(ГруппаПриемник, ПользовательСсылка) Экспорт
	
	Если ТипЗнч(ПользовательСсылка) = Тип("СправочникСсылка.ВнешниеПользователи") Тогда
		
		СвойстваГруппыПриемника = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
			ГруппаПриемник, "Назначение, ВсеОбъектыАвторизации");
		
		Если СвойстваГруппыПриемника.ВсеОбъектыАвторизации Тогда
			Возврат Ложь;
		КонецЕсли;
		
		НазначениеГруппыПриемника = СвойстваГруппыПриемника.Назначение.Выгрузить();
		
		ТипВнешнегоПользователя = ТипЗнч(ОбщегоНазначения.ЗначениеРеквизитаОбъекта(
			ПользовательСсылка, "ОбъектАвторизации"));
		ОписаниеТипаСсылки = Новый ОписаниеТипов(ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(ТипВнешнегоПользователя));
		Значение = ОписаниеТипаСсылки.ПривестиЗначение(Неопределено);
		
		Отбор = Новый Структура("ТипПользователей", Значение);
		Если НазначениеГруппыПриемника.НайтиСтроки(Отбор).Количество() <> 1 Тогда
			Возврат Ложь;
		КонецЕсли;
		
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

Процедура ДобавитьПользователяВГруппу(ГруппаВладелец, ПользовательСсылка, ИмяКолонкиСостава, Добавлен) Экспорт
	
	Если ГруппаВладелец = Справочники.ГруппыПользователей.ВсеПользователи
	 Или ГруппаВладелец = Справочники.ГруппыВнешнихПользователей.ВсеВнешниеПользователи Тогда
		Возврат;
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		Блокировка = Новый БлокировкаДанных;
		Если ИмяКолонкиСостава = "ВнешнийПользователь" Тогда
			ИмяТаблицы = "Справочник.ГруппыВнешнихПользователей";
		Иначе
			ИмяТаблицы = "Справочник.ГруппыПользователей";
		КонецЕсли;
		ЭлементБлокировки = Блокировка.Добавить(ИмяТаблицы);
		ЭлементБлокировки.УстановитьЗначение("Ссылка", ГруппаВладелец);
		Блокировка.Заблокировать();
		
		ГруппаВладелецОбъект = ГруппаВладелец.ПолучитьОбъект();
		Свойства = Новый Структура("ВсеОбъектыАвторизации", Ложь);
		ЗаполнитьЗначенияСвойств(Свойства, ГруппаВладелецОбъект);
		
		СтрокаСостава = ГруппаВладелецОбъект.Состав.Найти(ПользовательСсылка, ИмяКолонкиСостава);
		
		Если СтрокаСостава = Неопределено И Не Свойства.ВсеОбъектыАвторизации Тогда
			СтрокаСостава = ГруппаВладелецОбъект.Состав.Добавить();
			Если ИмяКолонкиСостава = "ВнешнийПользователь" Тогда
				СтрокаСостава.ВнешнийПользователь = ПользовательСсылка;
			Иначе
				СтрокаСостава.Пользователь = ПользовательСсылка;
			КонецЕсли;
			ГруппаВладелецОбъект.Записать();
			Добавлен = Истина;
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

Процедура УдалитьПользователяИзГруппы(ГруппаВладелец, ПользовательСсылка, ИмяКолонкиСостава, Удален = Ложь) Экспорт
	
	Если ГруппаВладелец = Справочники.ГруппыПользователей.ВсеПользователи
	 Или ГруппаВладелец = Справочники.ГруппыВнешнихПользователей.ВсеВнешниеПользователи Тогда
		Возврат;
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		Блокировка = Новый БлокировкаДанных;
		Если ИмяКолонкиСостава = "ВнешнийПользователь" Тогда
			ИмяТаблицы = "Справочник.ГруппыВнешнихПользователей";
		Иначе
			ИмяТаблицы = "Справочник.ГруппыПользователей";
		КонецЕсли;
		ЭлементБлокировки = Блокировка.Добавить(ИмяТаблицы);
		ЭлементБлокировки.УстановитьЗначение("Ссылка", ГруппаВладелец);
		Блокировка.Заблокировать();
		
		ГруппаВладелецОбъект = ГруппаВладелец.ПолучитьОбъект();
		
		СтрокаСостава = ГруппаВладелецОбъект.Состав.Найти(ПользовательСсылка, ИмяКолонкиСостава);
		Если СтрокаСостава <> Неопределено Тогда
			ГруппаВладелецОбъект.Состав.Удалить(СтрокаСостава);
			ГруппаВладелецОбъект.Записать();
			Удален = Истина;
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Формирует сообщение о результатах переноса пользователя.
//
// Параметры:
//  МассивПользователей - Массив - пользователи, которых нужно перенести в новую группу.
//  ГруппаПриемник      - СправочникСсылка.ГруппыПользователей - группа, в которую переносятся
//                        пользователи.
//  Перемещение         - Булево - если Истина, то пользователь удаляется из старой группы.
//  МассивНеПеремещенныхПользователей - Массив - пользователи, которых не удалось переместить в группу.
//  ГруппаИсточник      - СправочникСсылка.ГруппыПользователей - группа, из которой происходит
//                        перенос пользователей.
//
// Возвращаемое значение:
//  Строка - сообщение пользователю.
//
Функция ФормированиеСообщенияПользователю(МассивПользователей, ГруппаПриемник,
	                                      Перемещение, МассивНеПеремещенныхПользователей, ГруппаИсточник = Неопределено) Экспорт
	
	КоличествоПользователей = МассивПользователей.Количество();
	НаименованиеГруппы = Строка(ГруппаПриемник);
	СообщениеПользователю = Неопределено;
	КоличествоНеПеремещенныхПользователей = МассивНеПеремещенныхПользователей.Количество();
	
	УведомлениеПользователя = Новый Структура;
	УведомлениеПользователя.Вставить("Сообщение");
	УведомлениеПользователя.Вставить("ЕстьОшибки");
	УведомлениеПользователя.Вставить("Пользователи");
	
	Если КоличествоНеПеремещенныхПользователей > 0 Тогда
		
		СвойстваГруппыПриемника = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
			ГруппаПриемник, "Назначение, Наименование");
		
		НаименованиеГруппы = СвойстваГруппыПриемника.Наименование;
		НазначениеГруппыВнешнихПользователей = СвойстваГруппыПриемника.Назначение.Выгрузить();
		
		МассивПредставлений = Новый Массив;
		Для Каждого СтрокаНазначения Из НазначениеГруппыВнешнихПользователей Цикл
			
			МассивПредставлений.Добавить(НРег(Метаданные.НайтиПоТипу(
				ТипЗнч(СтрокаНазначения.ТипПользователей)).Синоним));
			
		КонецЦикла;
		
		ПредставлениеТипаОбъектаАвторизации = СтрСоединить(МассивПредставлений, ", ");
		
		Если КоличествоНеПеремещенныхПользователей = 1 Тогда
			
			СвойстваНеПеремещенногоПользователя = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
				МассивНеПеремещенныхПользователей[0], "Наименование, ОбъектАвторизации");
			
			Предмет = СвойстваНеПеремещенногоПользователя.Наименование;
			
			ТипВнешнегоПользователя = ТипЗнч(СвойстваНеПеремещенногоПользователя.ОбъектАвторизации);
			ОписаниеТипаСсылки = Новый ОписаниеТипов(ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(ТипВнешнегоПользователя));
			Значение = ОписаниеТипаСсылки.ПривестиЗначение(Неопределено);
		
			Отбор = Новый Структура("ТипПользователей", Значение);
			ТипПользователяСовпадаетСГруппой = (НазначениеГруппыВнешнихПользователей.НайтиСтроки(Отбор).Количество() = 1);
			
			УведомлениеПользователя.Пользователи = Неопределено;
			
			Если ТипПользователяСовпадаетСГруппой Тогда
				СообщениеПользователю = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Пользователь ""%1"" не может быть включен в группу ""%2"",
					           |т.к. у группы стоит признак ""Все пользователи заданного типа"".'"),
					Предмет, НаименованиеГруппы) + Символы.ПС;
			Иначе
				СообщениеПользователю = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Пользователь ""%1"" не может быть включен в группу ""%2"",
					           |т.к. в состав ее участников входят только %3.'"),
					Предмет, НаименованиеГруппы, ПредставлениеТипаОбъектаАвторизации) + Символы.ПС;
			КонецЕсли;
		Иначе
			УведомлениеПользователя.Пользователи = СтрСоединить(МассивНеПеремещенныхПользователей, Символы.ПС);
			
			СообщениеПользователю = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не все пользователи могут быть включены в группу ""%1"",
				           |т.к. в состав ее участников входят только %2
				           |или у группы стоит признак ""Все пользователи заданного типа"".'"),
				НаименованиеГруппы,
				ПредставлениеТипаОбъектаАвторизации);
		КонецЕсли;
		
		УведомлениеПользователя.Сообщение = СообщениеПользователю;
		УведомлениеПользователя.ЕстьОшибки = Истина;
		
		Возврат УведомлениеПользователя;
	КонецЕсли;
	
	Если КоличествоПользователей = 1 Тогда
		
		СтрокаОбъект = Строка(МассивПользователей[0]);
		
		Если ГруппаПриемник = Справочники.ГруппыПользователей.ВсеПользователи
		 Или ГруппаПриемник = Справочники.ГруппыВнешнихПользователей.ВсеВнешниеПользователи Тогда
			
			СообщениеПользователю = НСтр("ru = '""%1"" исключен из группы ""%2""'");
			НаименованиеГруппы = Строка(ГруппаИсточник);
			
		ИначеЕсли Перемещение Тогда
			СообщениеПользователю = НСтр("ru = '""%1"" перемещен в группу ""%2""'");
		Иначе
			СообщениеПользователю = НСтр("ru = '""%1"" включен в группу ""%2""'");
		КонецЕсли;
		
	ИначеЕсли КоличествоПользователей > 1 Тогда
		
		СтрокаОбъект = Формат(КоличествоПользователей, "ЧДЦ=0") + " "
			+ ПользователиСлужебныйКлиентСервер.ПредметЦелогоЧисла(КоличествоПользователей,
				"", НСтр("ru = 'пользователь,пользователя,пользователей,,,,,,0'"));
		
		Если ГруппаПриемник = Справочники.ГруппыПользователей.ВсеПользователи Тогда
			СообщениеПользователю = НСтр("ru = '%1 исключены из группы ""%2""'");
			НаименованиеГруппы = Строка(ГруппаИсточник);
			
		ИначеЕсли Перемещение Тогда
			СообщениеПользователю = НСтр("ru = '%1 перемещены в группу ""%2""'");
		Иначе
			СообщениеПользователю = НСтр("ru = '%1 включены в группу ""%2""'");
		КонецЕсли;
		
	КонецЕсли;
	
	Если СообщениеПользователю <> Неопределено Тогда
		СообщениеПользователю = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(СообщениеПользователю,
			СтрокаОбъект, НаименованиеГруппы);
	КонецЕсли;
	
	УведомлениеПользователя.Сообщение = СообщениеПользователю;
	УведомлениеПользователя.ЕстьОшибки = Ложь;
	
	Возврат УведомлениеПользователя;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Восстановление паролей пользователей

Процедура ЗаполнитьПочтуДляВосстановленияПароляУПользователейВФоне(ДополнительныеПараметры, АдресВоВременномХранилище) Экспорт
	
	СписокПользователей = ПользователиДляВключенияВосстановленияПароля();
	Для каждого ПользовательСсылка Из СписокПользователей Цикл
		ОбновитьПочтуДляВосстановленияПароля(ПользовательСсылка);
	КонецЦикла;
	
	СписокПользователей = ВнешниеПользователиДляВключенияВосстановленияПароля();
	Для каждого ВнешнийПользовательСсылка Из СписокПользователей Цикл
		
		ОбъектАвторизации = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ВнешнийПользовательСсылка, "ОбъектАвторизации");
		ОбновитьПочтуДляВосстановленияПароля(ВнешнийПользовательСсылка, ОбъектАвторизации);
		
	КонецЦикла;
	
КонецПроцедуры

Функция ДоступнаНастройкаВосстановленияПароля(УровеньДоступа) Экспорт
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если Не УровеньДоступа.ИзменениеТекущего
		 И Не УровеньДоступа.УправлениеСписком Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

// Параметры:
//  УровеньДоступа - Структура
//  Объект - СправочникОбъект.Пользователи
//         - СправочникОбъект.ВнешниеПользователи
// 
// Возвращаемое значение:
//  Булево
//
Функция ИнтерактивноЗапрашиватьВводПароля(УровеньДоступа, Объект) Экспорт
	
	Если Пользователи.ЭтоПолноправныйПользователь()
		Или УровеньДоступа.УправлениеСписком
		Или НЕ ЗначениеЗаполнено(Объект.Ссылка) Тогда
			Возврат Ложь;
	ИначеЕсли ЗначениеЗаполнено(Объект.ИдентификаторПользователяИБ) 
		И УровеньДоступа.ИзменениеТекущего Тогда
		
			УстановитьПривилегированныйРежим(Истина);
			ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(Объект.ИдентификаторПользователяИБ);
			УстановитьПривилегированныйРежим(Ложь);
			
			Если ПользовательИБ <> Неопределено Тогда
				Возврат ПользовательИБ.ПарольУстановлен;
			КонецЕсли;
		
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

// Параметры:
//  УровеньДоступа - Структура
//  Объект - СправочникОбъект.Пользователи
//         - СправочникОбъект.ВнешниеПользователи
// 
// Возвращаемое значение:
//  Булево
//
Функция ДоступноРедактированиеПочтыДляВосстановленияПароля(УровеньДоступа, Объект) Экспорт
	
	Если Пользователи.ЭтоПолноправныйПользователь()
		Или УровеньДоступа.ИзменениеТекущего
		Или Не ЗначениеЗаполнено(Объект.Ссылка)
		Или (УровеньДоступа.УправлениеСписком И Объект.Подготовлен) Тогда
			Возврат Истина;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

Функция ПользователиДляВключенияВосстановленияПароля() Экспорт
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.КонтактнаяИнформация") Тогда
		Возврат Новый Массив;
	КонецЕсли;
	
	ПользователиИБСПочтой = ПользователиСЭлектроннойПочтойДляВосстановления();
	
	Если ВнешниеПользователи.ИспользоватьВнешнихПользователей() Тогда
		Возврат ПользователиДляВключенияВосстановленияПароляСУчетомВнешнихПользователей(ПользователиИБСПочтой);
	КонецЕсли;
	
	МодульУправлениеКонтактнойИнформацией = ОбщегоНазначения.ОбщийМодуль("УправлениеКонтактнойИнформацией");
	
	Запрос = Новый Запрос;
	Запрос.Текст = "ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	МАКСИМУМ(ПользователиКонтактнаяИнформация.Ссылка) КАК Ссылка,
	|	ПользователиКонтактнаяИнформация.Представление КАК Представление,
	|	Пользователи.ИдентификаторПользователяИБ КАК ИдентификаторПользователяИБ
	|ИЗ
	|	Справочник.Пользователи.КонтактнаяИнформация КАК ПользователиКонтактнаяИнформация
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Пользователи КАК Пользователи
	|		ПО ПользователиКонтактнаяИнформация.Ссылка = Пользователи.Ссылка
	|ГДЕ
	|	ПользователиКонтактнаяИнформация.Вид = &ВидКонтактнойИнформации
	|	И Пользователи.Недействителен = ЛОЖЬ
	|	И Пользователи.Служебный = ЛОЖЬ
	|
	|СГРУППИРОВАТЬ ПО
	|	ПользователиКонтактнаяИнформация.Представление,
	|	Пользователи.ИдентификаторПользователяИБ";
	
	Запрос.УстановитьПараметр("ВидКонтактнойИнформации",
	МодульУправлениеКонтактнойИнформацией.ВидКонтактнойИнформацииПоИмени("EmailПользователя"));
	
	РезультатЗапроса = Запрос.Выполнить().Выгрузить();
	
	Возврат СписокПользователейДляОбновления(ПользователиИБСПочтой, РезультатЗапроса);
	
КонецФункции

Функция ПользователиДляВключенияВосстановленияПароляСУчетомВнешнихПользователей(ПользователиИБСПочтой)
	
	ТипыВнешнихПользователей = Метаданные.ОпределяемыеТипы.ВнешнийПользователь.Тип.Типы();
	
	Если ТипыВнешнихПользователей.Количество() = 0 Тогда
		Возврат Новый Массив;
	КонецЕсли;
	
	МодульУправлениеКонтактнойИнформацией = ОбщегоНазначения.ОбщийМодуль("УправлениеКонтактнойИнформацией");
	
	ШаблонЗапроса = "ВЫБРАТЬ
		|	КонтактнаяИнформацияВнешнегоПользователя.Представление КАК Почта
		|ПОМЕСТИТЬ ВременнаяТаблица
		|ИЗ
		|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
		|		ЛЕВОЕ СОЕДИНЕНИЕ #КонтактнаяИнформация КАК КонтактнаяИнформацияВнешнегоПользователя
		|		ПО (ВнешниеПользователи.ОбъектАвторизации = КонтактнаяИнформацияВнешнегоПользователя.Ссылка)
		|ГДЕ
		|	ВнешниеПользователи.Недействителен = ЛОЖЬ
		|	И КонтактнаяИнформацияВнешнегоПользователя.Тип = &ТипКонтактнойИнформации";
	
	НаборЗапросов = Новый Массив;
	
	Для каждого ТипВнешнегоПользователя Из ТипыВнешнихПользователей Цикл
		
		Если Не МодульУправлениеКонтактнойИнформацией.СодержитКонтактнуюИнформацию(ТипВнешнегоПользователя) Тогда
			Продолжить;
		КонецЕсли;
		
		ИмяТаблицы = Метаданные.НайтиПоТипу(ТипВнешнегоПользователя).ПолноеИмя() + ".КонтактнаяИнформация";
		НаборЗапросов.Добавить(СтрЗаменить(ШаблонЗапроса, "#КонтактнаяИнформация", ИмяТаблицы));
		
		ШаблонЗапроса = СтрЗаменить(ШаблонЗапроса, "ПОМЕСТИТЬ ВременнаяТаблица", "");
	КонецЦикла;
	
	Если НаборЗапросов.Количество() = 0 Тогда
		Возврат Новый Массив;
	КонецЕсли;
	
	ТекстЗапроса = СтрСоединить(НаборЗапросов, Символы.ПС + " ОБЪЕДИНИТЬ ВСЕ " + Символы.ПС);
	
	ТекстЗапроса = ТекстЗапроса + ОбщегоНазначения.РазделительПакетаЗапросов() + "
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	МАКСИМУМ(Пользователи.Ссылка) КАК Ссылка,
	|	ПользователиКонтактнаяИнформация.Представление КАК Представление,
	|	Пользователи.ИдентификаторПользователяИБ КАК ИдентификаторПользователяИБ
	|ИЗ
	|	Справочник.Пользователи.КонтактнаяИнформация КАК ПользователиКонтактнаяИнформация
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Пользователи КАК Пользователи
	|		ПО (ПользователиКонтактнаяИнформация.Ссылка = Пользователи.Ссылка)
	|		ЛЕВОЕ СОЕДИНЕНИЕ ВременнаяТаблица КАК ВременнаяТаблица
	|		ПО (ПользователиКонтактнаяИнформация.Представление = ВременнаяТаблица.Почта)
	|
	|ГДЕ
	|	ПользователиКонтактнаяИнформация.Вид = &ВидКонтактнойИнформации
	|	И Пользователи.Недействителен = ЛОЖЬ
	|	И Пользователи.Служебный = ЛОЖЬ
	|	И ВременнаяТаблица.Почта ЕСТЬ NULL
	|
	|СГРУППИРОВАТЬ ПО
	|	ПользователиКонтактнаяИнформация.Представление, Пользователи.ИдентификаторПользователяИБ
	|
	|ИМЕЮЩИЕ
	|	КОЛИЧЕСТВО(ПользователиКонтактнаяИнформация.Ссылка) = 1";
	
	Запрос = Новый Запрос(ТекстЗапроса);
	
	Запрос.Параметры.Вставить("ТипКонтактнойИнформации",
		МодульУправлениеКонтактнойИнформацией.ТипКонтактнойИнформацииПоНаименованию("АдресЭлектроннойПочты"));
	Запрос.УстановитьПараметр("ВидКонтактнойИнформации",
			МодульУправлениеКонтактнойИнформацией.ВидКонтактнойИнформацииПоИмени("EmailПользователя"));
	
	РезультатЗапроса = Запрос.Выполнить().Выгрузить();
	
	Возврат СписокПользователейДляОбновления(ПользователиИБСПочтой, РезультатЗапроса);
	
КонецФункции

Функция СписокПользователейДляОбновления(ПользователиИБСПочтой, РезультатЗапроса)

	Если ПользователиИБСПочтой.Количество() = 0 Тогда
		Возврат РезультатЗапроса.ВыгрузитьКолонку("Ссылка");
	КонецЕсли;
	
	СписокПользователей = Новый Массив;
	
	Для Каждого ПользователяДляОбновления Из РезультатЗапроса Цикл
		СведенияОПользователе = ПользователиИБСПочтой[ПользователяДляОбновления.Представление];
		
		Если СведенияОПользователе = Неопределено Тогда
			СписокПользователей.Добавить(ПользователяДляОбновления.Ссылка);
			Продолжить;
		КонецЕсли;
		
		Если СтрСравнить(ПользователяДляОбновления.ИдентификаторПользователяИБ, СведенияОПользователе.УникальныйИдентификатор) = 0 Тогда
			Продолжить;
		КонецЕсли;
		
		ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Для пользователя ""%1"" не может быть установлена почта %2
			           |для восстановления пароля, т.к. она используется для пользователя
			           |%3 (%4)'"),
			Строка(ПользователяДляОбновления.Ссылка),
			СведенияОПользователе.АдресЭлектроннойПочты,
			СведенияОПользователе.Имя,
			СведенияОПользователе.ПолноеИмя);
			
		ЗаписьЖурналаРегистрации(ОбновлениеИнформационнойБазы.СобытиеЖурналаРегистрации(),
			УровеньЖурналаРегистрации.Предупреждение,
			Метаданные.Справочники.Пользователи,
			ПользователяДляОбновления.Ссылка,
			ТекстСообщения);
	КонецЦикла;
	
	Возврат СписокПользователей;
	
КонецФункции

Функция ВнешниеПользователиДляВключенияВосстановленияПароля() Экспорт
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.КонтактнаяИнформация") Тогда
		Возврат Новый Массив;
	КонецЕсли;
	
	ТипыВнешнихПользователей = Метаданные.ОпределяемыеТипы.ВнешнийПользователь.Тип.Типы();
	
	Если ТипыВнешнихПользователей.Количество() = 0 Тогда
		Возврат Новый Массив;
	КонецЕсли;
	
	МодульУправлениеКонтактнойИнформацией = ОбщегоНазначения.ОбщийМодуль("УправлениеКонтактнойИнформацией");
	НаборЗапросов = Новый Массив;
	
	ТекстЗапроса = "ВЫБРАТЬ
	|	ПользователиКонтактнаяИнформация.Ссылка КАК Ссылка,
	|	ПользователиКонтактнаяИнформация.Представление КАК Представление,
	|	Пользователи.ИдентификаторПользователяИБ КАК ИдентификаторПользователяИБ
	|ПОМЕСТИТЬ ВременнаяТаблица
	|ИЗ
	|	Справочник.Пользователи.КонтактнаяИнформация КАК ПользователиКонтактнаяИнформация
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Пользователи КАК Пользователи
	|		ПО (ПользователиКонтактнаяИнформация.Ссылка = Пользователи.Ссылка)
	|ГДЕ
	|	ПользователиКонтактнаяИнформация.Вид = &ВидКонтактнойИнформации
	|	И Пользователи.Недействителен = ЛОЖЬ
	|	И Пользователи.Служебный = ЛОЖЬ";
	
	НаборЗапросов.Добавить(ТекстЗапроса);
	
	ШаблонЗапроса = "ВЫБРАТЬ
	|	ВнешниеПользователи.Ссылка КАК Ссылка,
	|	КонтактнаяИнформацияВнешнегоПользователя.Представление, 
	|	ВнешниеПользователи.ИдентификаторПользователяИБ КАК ИдентификаторПользователяИБ
	|ИЗ
	|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
	|		ЛЕВОЕ СОЕДИНЕНИЕ #КонтактнаяИнформация КАК КонтактнаяИнформацияВнешнегоПользователя
	|		ПО (ВнешниеПользователи.ОбъектАвторизации = КонтактнаяИнформацияВнешнегоПользователя.Ссылка)
	|ГДЕ
	|	ВнешниеПользователи.Недействителен = ЛОЖЬ
	|	И КонтактнаяИнформацияВнешнегоПользователя.Тип = &ТипКонтактнойИнформации";

	Для каждого ТипВнешнегоПользователя Из ТипыВнешнихПользователей Цикл
		
		Если Не МодульУправлениеКонтактнойИнформацией.СодержитКонтактнуюИнформацию(ТипВнешнегоПользователя) Тогда
			Продолжить;
		КонецЕсли;
		
		ИмяТаблицы = Метаданные.НайтиПоТипу(ТипВнешнегоПользователя).ПолноеИмя() + ".КонтактнаяИнформация";
		НаборЗапросов.Добавить(СтрЗаменить(ШаблонЗапроса, "#КонтактнаяИнформация", ИмяТаблицы));
		
	КонецЦикла;
	
	Если НаборЗапросов.Количество() < 2 Тогда
		Возврат Новый Массив;
	КонецЕсли;
	
	ТекстЗапроса = СтрСоединить(НаборЗапросов, Символы.ПС + " ОБЪЕДИНИТЬ ВСЕ " + Символы.ПС);
	
	ТекстЗапроса = ТекстЗапроса + ОбщегоНазначения.РазделительПакетаЗапросов() + "
	|ВЫБРАТЬ
	|	МАКСИМУМ(ВременнаяТаблица.Ссылка) КАК Ссылка,
	|	ВременнаяТаблица.Представление КАК Представление,
	|	ВременнаяТаблица.ИдентификаторПользователяИБ КАК ИдентификаторПользователяИБ
	|ИЗ
	|	ВременнаяТаблица КАК ВременнаяТаблица
	|	
	|СГРУППИРОВАТЬ ПО
	|	ВременнаяТаблица.Представление, ВременнаяТаблица.ИдентификаторПользователяИБ
	|
	|ИМЕЮЩИЕ
	|	КОЛИЧЕСТВО(ВременнаяТаблица.Ссылка) = 1 
	|	И ТИПЗНАЧЕНИЯ(МАКСИМУМ(ВременнаяТаблица.Ссылка)) <> ТИП(Справочник.Пользователи)";
	
	Запрос = Новый Запрос(ТекстЗапроса);
	
	Запрос.Параметры.Вставить("ТипКонтактнойИнформации",
		МодульУправлениеКонтактнойИнформацией.ТипКонтактнойИнформацииПоНаименованию("АдресЭлектроннойПочты"));
	Запрос.УстановитьПараметр("ВидКонтактнойИнформации",
			МодульУправлениеКонтактнойИнформацией.ВидКонтактнойИнформацииПоИмени("EmailПользователя"));
	
	РезультатЗапроса = Запрос.Выполнить().Выгрузить();
	
	ПользователиИБСПочтой = ПользователиСЭлектроннойПочтойДляВосстановления();
	
	Возврат СписокПользователейДляОбновления(ПользователиИБСПочтой, РезультатЗапроса);
	
КонецФункции

Функция ОбновитьПочтуДляВосстановленияПароля(ПользовательСсылка, ОбъектАвторизации = Неопределено) Экспорт
	
	Результат =  Новый Структура;
	Результат.Вставить("Статус",      "ОбновлениеНеТребуется");
	Результат.Вставить("ТекстОшибки", "");
	
	МодульУправлениеКонтактнойИнформацией = ОбщегоНазначения.ОбщийМодуль("УправлениеКонтактнойИнформацией");
	ИдентификаторПользователяИБ = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ПользовательСсылка, "ИдентификаторПользователяИБ");
	
	ПользовательИБ = Пользователи.СвойстваПользователяИБ(ИдентификаторПользователяИБ);
	Если ПользовательИБ = Неопределено Или ЗначениеЗаполнено(ПользовательИБ.АдресЭлектроннойПочты) Тогда
		Возврат Результат;
	КонецЕсли;
	
	Если ОбъектАвторизации = Неопределено Тогда
		
		ВладелецКИ                 = ПользовательСсылка;
		ТипИлиВидЭлектроннойПочты  = МодульУправлениеКонтактнойИнформацией.ВидКонтактнойИнформацииПоИмени("EmailПользователя");
		ПолноеИмяОбъектаМетаданных = Метаданные.Справочники.Пользователи.ПолноеИмя();
		
	Иначе
		
		Если Не МодульУправлениеКонтактнойИнформацией.СодержитКонтактнуюИнформацию(ОбъектАвторизации) Тогда
			Возврат Результат;
		КонецЕсли;
		ВладелецКИ                 = ОбъектАвторизации;
		ТипИлиВидЭлектроннойПочты  = МодульУправлениеКонтактнойИнформацией.ТипКонтактнойИнформацииПоНаименованию("АдресЭлектроннойПочты");
		ПолноеИмяОбъектаМетаданных = ОбъектАвторизации.Метаданные().ПолноеИмя();
		
	КонецЕсли;
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить(ПолноеИмяОбъектаМетаданных);
	ЭлементБлокировки.УстановитьЗначение("Ссылка", ВладелецКИ);
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
	
	ПочтаДляВосстановленияПароля = "";
	ПредставлениеСсылки = Строка(ПользовательСсылка);
	НачатьТранзакцию();
	Попытка
		
		Блокировка.Заблокировать();
		
		КонтактнаяИнформацияПользователя = МодульУправлениеКонтактнойИнформацией.КонтактнаяИнформацияОбъекта(ВладелецКИ,
			ТипИлиВидЭлектроннойПочты, ТекущаяДатаСеанса(), Ложь);
		
		Если КонтактнаяИнформацияПользователя.Количество() > 0 Тогда
			
			СтрокаСПочтой = КонтактнаяИнформацияПользователя[0];
			
			Если ЗначениеЗаполнено(СтрокаСПочтой.Значение) Тогда
				ПочтаДляВосстановленияПароля = МодульУправлениеКонтактнойИнформацией.АдресЭлектроннойПочты(СтрокаСПочтой.Значение);
				Если Не ОбщегоНазначенияКлиентСервер.АдресЭлектроннойПочтыСоответствуетТребованиям(ПочтаДляВосстановленияПароля) Тогда
					ПочтаДляВосстановленияПароля = "";
				КонецЕсли;
			КонецЕсли;
			
			Если ПустаяСтрока(ПочтаДляВосстановленияПароля) И ЗначениеЗаполнено(СтрокаСПочтой.ЗначенияПолей) Тогда
				ПочтаДляВосстановленияПароля = МодульУправлениеКонтактнойИнформацией.АдресЭлектроннойПочты(СтрокаСПочтой.ЗначенияПолей);
				Если Не ОбщегоНазначенияКлиентСервер.АдресЭлектроннойПочтыСоответствуетТребованиям(ПочтаДляВосстановленияПароля) Тогда
					ПочтаДляВосстановленияПароля = "";
				КонецЕсли;
			КонецЕсли;
			
			Если ПустаяСтрока(ПочтаДляВосстановленияПароля) И ЗначениеЗаполнено(СтрокаСПочтой.Представление) Тогда
				ПочтаДляВосстановленияПароля = СтрокаСПочтой.Представление;
			КонецЕсли;
			
			Если ОбщегоНазначенияКлиентСервер.АдресЭлектроннойПочтыСоответствуетТребованиям(ПочтаДляВосстановленияПароля) Тогда
				
				ПользовательИБ.АдресЭлектроннойПочты          = ПочтаДляВосстановленияПароля;
				ПользовательИБ.ЗапрещеноВосстанавливатьПароль = Ложь;
				Пользователи.УстановитьСвойстваПользователяИБ(ИдентификаторПользователяИБ, ПользовательИБ);
				
			КонецЕсли;
			
		КонецЕсли;
		
		Результат.Статус = "Обновлен";
		ЗафиксироватьТранзакцию();
		
	Исключение
		ОтменитьТранзакцию();
		
		// Если не удалось обработать пользователя, повторяем попытку снова.
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		
		Результат.Статус = "Ошибка";
		Результат.ТекстОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке);
		
		ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось обработать пользователя %1 по причине: %2'"),
			ПредставлениеСсылки, ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
			
		ЗаписьЖурналаРегистрации(ОбновлениеИнформационнойБазы.СобытиеЖурналаРегистрации(), УровеньЖурналаРегистрации.Предупреждение,
			Метаданные.Справочники.Пользователи, ПользовательСсылка, ТекстСообщения);
			
		Если ЗначениеЗаполнено(ПочтаДляВосстановленияПароля)
			 И СтрНайти(ТекстСообщения, ПочтаДляВосстановленияПароля) > 0 Тогда
				Уточнение = НСтр("ru = 'Адрес электронной почты %1 занят другим пользователем и не может быть использован для восстановления пароля.'");
				Уточнение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Уточнение, ПочтаДляВосстановленияПароля);
				ОбновлениеИнформационнойБазы.ЗарегистрироватьПроблемуСДанными(ПользовательСсылка, Уточнение);
		КонецЕсли;
		
	КонецПопытки;
	
	Возврат Результат;
	
КонецФункции

Функция ПользователиСЭлектроннойПочтойДляВосстановления()
	
	ПользователиИБСПочтой = Новый Соответствие;
	
	ПользователиИБ = ПользователиИнформационнойБазы.ПолучитьПользователей();
	Для Каждого ПользовательИБ Из ПользователиИБ Цикл
		
		СведенияОПользователе = СведенияОПользователеДляОбновленияПочтыДляВосстановления();
		ЗаполнитьЗначенияСвойств(СведенияОПользователе, ПользовательИБ);
		
		Если ЗначениеЗаполнено(СведенияОПользователе.АдресЭлектроннойПочты) Тогда
			ПользователиИБСПочтой.Вставить(СведенияОПользователе.АдресЭлектроннойПочты, СведенияОПользователе);
		КонецЕсли;
		
	КонецЦикла;
	
	Возврат ПользователиИБСПочтой;
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//   * АдресЭлектроннойПочты - Строка
//   * УникальныйИдентификатор - Строка
//   * Имя - Строка
//   * ПолноеИмя - Строка
//
Функция СведенияОПользователеДляОбновленияПочтыДляВосстановления()
	
	Результат = Новый Структура;
	Результат.Вставить("АдресЭлектроннойПочты", "");
	Результат.Вставить("УникальныйИдентификатор", "");
	Результат.Вставить("Имя", "");
	Результат.Вставить("ПолноеИмя", "");
	
	Возврат Результат;
	
КонецФункции

// Параметры:
//  ПользовательИБ - ПользовательИнформационнойБазы
//
// Возвращаемое значение:
//  Строка
//
Функция ХешСуммаРолейПользователяИБ(ПользовательИБ)
	
	Хеширование = Новый ХешированиеДанных(ХешФункция.SHA256);
	Хеширование.Добавить(ЗначениеВСтрокуВнутр(ПользовательИБ.Роли));
	
	Возврат Base64Строка(Хеширование.ХешСумма);
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Универсальные процедуры и функции.

// Возвращает несовпадающие значения в колонке таблиц значений.
//
// Параметры:
//  ИмяКолонки - Строка - имя сравниваемой колонки.
//  Таблица1   - ТаблицаЗначений
//  Таблица2   - ТаблицаЗначений
//
// Возвращаемое значение:
//  Массив - значения, которые есть только в колонке только одной из таблиц.
// 
Функция РазличияЗначенийКолонки(ИмяКолонки, Таблица1, Таблица2) Экспорт
	
	Если ТипЗнч(Таблица1) <> Тип("ТаблицаЗначений")
	   И ТипЗнч(Таблица2) <> Тип("ТаблицаЗначений") Тогда
		
		Возврат Новый Массив;
	КонецЕсли;
	
	Если ТипЗнч(Таблица1) <> Тип("ТаблицаЗначений") Тогда
		Возврат Таблица2.ВыгрузитьКолонку(ИмяКолонки);
	КонецЕсли;
	
	Если ТипЗнч(Таблица2) <> Тип("ТаблицаЗначений") Тогда
		Возврат Таблица1.ВыгрузитьКолонку(ИмяКолонки);
	КонецЕсли;
	
	Таблица11 = Таблица1.Скопировать(, ИмяКолонки);
	Таблица11.Свернуть(ИмяКолонки);
	
	Таблица22 = Таблица2.Скопировать(, ИмяКолонки);
	Таблица22.Свернуть(ИмяКолонки);
	
	Для Каждого Строка Из Таблица22 Цикл
		НоваяСтрока = Таблица11.Добавить();
		НоваяСтрока[ИмяКолонки] = Строка[ИмяКолонки];
	КонецЦикла;
	
	Таблица11.Колонки.Добавить("Признак");
	Таблица11.ЗаполнитьЗначения(1, "Признак");
	
	Таблица11.Свернуть(ИмяКолонки, "Признак");
	
	Отбор = Новый Структура("Признак", 1);
	Таблица = Таблица11.Скопировать(Таблица11.НайтиСтроки(Отбор));
	
	Возврат Таблица.ВыгрузитьКолонку(ИмяКолонки);
	
КонецФункции

Процедура ВыбратьПользователейГруппы(ВыбранныеЭлементы, ХранимыеПараметры, ПолеСписка) Экспорт 
	
	Если ВыбранныеЭлементы.Количество() = 0 Тогда 
		Возврат;
	КонецЕсли;
	
	ВыбранныйЭлемент = ВыбранныеЭлементы[0].ВыбранныйЭлемент;
	ТипВыбранногоЭлемента = ТипЗнч(ВыбранныйЭлемент);
	
	Если ТипВыбранногоЭлемента = Тип("СправочникСсылка.ГруппыПользователей")
			И ХранимыеПараметры.ВыборГруппПользователей
		Или ТипВыбранногоЭлемента = Тип("СправочникСсылка.ГруппыВнешнихПользователей")
			И ХранимыеПараметры.ВыборГруппВнешнихПользователей
		Или ТипВыбранногоЭлемента <> Тип("СправочникСсылка.ГруппыПользователей")
			И ТипВыбранногоЭлемента <> Тип("СправочникСсылка.ГруппыВнешнихПользователей") Тогда 
		
		Возврат;
	КонецЕсли;
	
	ПользователиГруппы = ПользователиГруппы(ПолеСписка);
	
	ВыбранныеЭлементы.Очистить();
	
	Для Каждого ПользовательГруппы Из ПользователиГруппы Цикл 
		
		Элемент = Новый Структура;
		Элемент.Вставить("ВыбранныйЭлемент", ПользовательГруппы.Ссылка);
		Элемент.Вставить("НомерКартинки", ПользовательГруппы.НомерКартинки);
		
		ВыбранныеЭлементы.Добавить(Элемент);
		
	КонецЦикла;
	
КонецПроцедуры

Функция ПользователиГруппы(ПолеСписка)
	
	Схема = ПолеСписка.ПолучитьИсполняемуюСхемуКомпоновкиДанных();
	Настройки = ПолеСписка.ПолучитьИсполняемыеНастройкиКомпоновкиДанных();
	
	ДобавитьПоляПользователейГруппы(Настройки);
	
	КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных();
	МакетКомпоновки = КомпоновщикМакета.Выполнить(
		Схема, Настройки,,, Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
	
	ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
	ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
	
	ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
	
	Возврат ПроцессорВывода.Вывести(ПроцессорКомпоновки);
	
КонецФункции

Процедура ДобавитьПоляПользователейГруппы(Настройки)
	
	ДобавляемыеПоля = СтрРазделить("Ссылка, НомерКартинки", ", ", Ложь);
	НастройкиСтруктура = Настройки.Структура[0]; // ГруппировкаКомпоновкиДанных
	ВыбранныеПоля = НастройкиСтруктура.Выбор;
	
	Для Каждого ДобавляемоеПоле Из ДобавляемыеПоля Цикл 
		
		ДоступноеПоле = ВыбранныеПоля.ДоступныеПоляВыбора.НайтиПоле(Новый ПолеКомпоновкиДанных(ДобавляемоеПоле));
		
		Если ДоступноеПоле = Неопределено Тогда 
			Продолжить;
		КонецЕсли;
		
		ПолеНайдено = Ложь;
		
		Для Каждого Элемент Из ВыбранныеПоля.Элементы Цикл 
			
			Если Элемент.Поле = ДоступноеПоле.Поле Тогда 
				ПолеНайдено = Истина;
				Прервать;
			КонецЕсли;
			
		КонецЦикла;
		
		Если ПолеНайдено Тогда 
			Продолжить;
		КонецЕсли;
		
		ВыбранноеПоле = ВыбранныеПоля.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
		ЗаполнитьЗначенияСвойств(ВыбранноеПоле, ДоступноеПоле);
		
	КонецЦикла;
	
КонецПроцедуры

// Параметры:
//  Список - ДинамическийСписок
//  ИмяПоля - Строка
//
Процедура ОграничитьИспользованиеЗаполняемогоПоляДинамическогоСписка(Список, ИмяПоля) Экспорт
	
	Поля = Список.ПолучитьОграниченияИспользованияВГруппировке();
	Если Поля.Найти(ИмяПоля) = Неопределено Тогда
		Поля.Добавить(ИмяПоля);
		Список.УстановитьОграниченияИспользованияВГруппировке(Поля);
	КонецЕсли;
	
	Поля = Список.ПолучитьОграниченияИспользованияВОтборе();
	Если Поля.Найти(ИмяПоля) = Неопределено Тогда
		Поля.Добавить(ИмяПоля);
		Список.УстановитьОграниченияИспользованияВОтборе(Поля);
	КонецЕсли;
	
	Поля = Список.ПолучитьОграниченияИспользованияВПорядке();
	Если Поля.Найти(ИмяПоля) = Неопределено Тогда
		Поля.Добавить(ИмяПоля);
		Список.УстановитьОграниченияИспользованияВПорядке(Поля);
	КонецЕсли;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Обновление информационной базы.

// Вызывается при переходе на версию конфигурации 2.1.3.16.
Процедура ОбновитьПредопределенныеВидыКонтактнойИнформацииПользователей() Экспорт
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.КонтактнаяИнформация") Тогда
		Возврат;
	КонецЕсли;
	
	МодульУправлениеКонтактнойИнформацией = ОбщегоНазначения.ОбщийМодуль("УправлениеКонтактнойИнформацией");
	
	ПараметрыВида = МодульУправлениеКонтактнойИнформацией.ПараметрыВидаКонтактнойИнформации("АдресЭлектроннойПочты");
	ПараметрыВида.Вид = "EmailПользователя";
	ПараметрыВида.МожноИзменятьСпособРедактирования = Истина;
	ПараметрыВида.РазрешитьВводНесколькихЗначений = Истина;
	ПараметрыВида.Порядок = 1;
	МодульУправлениеКонтактнойИнформацией.УстановитьСвойстваВидаКонтактнойИнформации(ПараметрыВида);
	
	ПараметрыВида = МодульУправлениеКонтактнойИнформацией.ПараметрыВидаКонтактнойИнформации("Телефон");
	ПараметрыВида.Вид = "ТелефонПользователя";
	ПараметрыВида.МожноИзменятьСпособРедактирования = Истина;
	ПараметрыВида.РазрешитьВводНесколькихЗначений = Истина;
	ПараметрыВида.Порядок = 2;
	МодульУправлениеКонтактнойИнформацией.УстановитьСвойстваВидаКонтактнойИнформации(ПараметрыВида);
	
КонецПроцедуры

Процедура ПеренестиНастройкиДлиныИСложностиПаролейКонфигуратора() Экспорт
	
	РазделениеВключено = ОбщегоНазначения.РазделениеВключено();
	
	Блокировка = Новый БлокировкаДанных;
	Блокировка.Добавить("Константа.НастройкиВходаПользователей");
	Если Не РазделениеВключено Тогда
		Блокировка.Добавить("Константа.УдалитьНастройкиВходаПользователей");
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		СтарыеНастройки = Неопределено;
		Если Не РазделениеВключено Тогда
			СтарыеНастройки = Константы.УдалитьНастройкиВходаПользователей.Получить().Получить();
			Константы.УдалитьНастройкиВходаПользователей.Установить(Новый ХранилищеЗначения(Неопределено));
		КонецЕсли;
		Если СтарыеНастройки = Неопределено Тогда
			СтарыеНастройки = Константы.НастройкиВходаПользователей.Получить().Получить();
		КонецЕсли;
		
		НовыеНастройки = НастройкиВхода();
		
		Если Не Пользователи.ОбщиеНастройкиВходаИспользуются() Тогда
			НовыеНастройки.Общие.Вставить("ОбновитьТолькоКонстанту");
			НовыеНастройки.Пользователи.Вставить("ОбновитьТолькоКонстанту");
			НовыеНастройки.ВнешниеПользователи.Вставить("ОбновитьТолькоКонстанту");
		КонецЕсли;
		
		Если ТипЗнч(СтарыеНастройки) <> Тип("Структура")
		 Или Не СтарыеНастройки.Свойство("Пользователи") Тогда
			ОписаниеОбщихНастроек = Пользователи.НовоеОписаниеОбщихНастроекВхода();
			НовыеНастройки.Общие.КоличествоПопытокВводаПароляДоБлокировки =
				ОписаниеОбщихНастроек.КоличествоПопытокВводаПароляДоБлокировки;
			НовыеНастройки.Общие.ДлительностьБлокировкиВводаПароля =
				ОписаниеОбщихНастроек.ДлительностьБлокировкиВводаПароля;
		КонецЕсли;
		
		Если ТипЗнч(СтарыеНастройки) = Тип("Структура") Тогда
			Если СтарыеНастройки.Свойство("Пользователи")
			   И ТипЗнч(СтарыеНастройки.Пользователи) = Тип("Структура") Тогда
				ЗаполнитьЗначенияСвойств(НовыеНастройки.Пользователи, СтарыеНастройки.Пользователи);
				Пользователи.УстановитьНастройкиВхода(НовыеНастройки.Пользователи);
			КонецЕсли;
			Если СтарыеНастройки.Свойство("ВнешниеПользователи")
			   И ТипЗнч(СтарыеНастройки.ВнешниеПользователи) = Тип("Структура") Тогда
				ЗаполнитьЗначенияСвойств(НовыеНастройки.ВнешниеПользователи, СтарыеНастройки.ВнешниеПользователи);
				Пользователи.УстановитьНастройкиВхода(НовыеНастройки.ВнешниеПользователи, Истина);
			КонецЕсли;
			Совпадают = Истина;
			Для Каждого КлючИЗначение Из НовыеНастройки.Пользователи Цикл
				Если КлючИЗначение.Значение <> НовыеНастройки.ВнешниеПользователи[КлючИЗначение.Ключ] Тогда
					Совпадают = Ложь;
					Прервать;
				КонецЕсли;
			КонецЦикла;
			Если Не Совпадают Тогда
				НовыеНастройки.Общие.ОтдельныеНастройкиДляВнешнихПользователей = Истина;
			КонецЕсли;
		КонецЕсли;
		
		Пользователи.УстановитьОбщиеНастройкиВхода(НовыеНастройки.Общие);
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Вызывается при переходе на новую версию конфигурации 2.4.1.1.
Процедура ДобавитьАдминистраторамПравоОткрытияВнешнихОтчетовИОбработок() Экспорт
	
	ДобавляемаяРоль = Метаданные.Роли.ИнтерактивноеОткрытиеВнешнихОтчетовИОбработок;
	РольАдминистратора = Метаданные.Роли.АдминистраторСистемы;
	ПользователиИБ = ПользователиИнформационнойБазы.ПолучитьПользователей();
	
	Для Каждого ПользовательИБ Из ПользователиИБ Цикл
		
		Если ПользовательИБ.Роли.Содержит(РольАдминистратора)
		   И Не ПользовательИБ.Роли.Содержит(ДобавляемаяРоль) Тогда
			
			ПользовательИБ.Роли.Добавить(ДобавляемаяРоль);
			ПользовательИБ.Записать();
		КонецЕсли;
		
	КонецЦикла;
	
КонецПроцедуры

// Вызывается при переходе на новую версию конфигурации 2.4.1.1.
Процедура ПереименоватьКлючХраненияРешенияПоОткрытиюВнешнихОтчетовИОбработок() Экспорт
	
	Блокировка = Новый БлокировкаДанных;
	Блокировка.Добавить("Константа.ПараметрыАдминистрированияИБ");
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		
		ПараметрыАдминистрированияИБ = Константы.ПараметрыАдминистрированияИБ.Получить().Получить();
		
		Если ТипЗнч(ПараметрыАдминистрированияИБ) = Тип("Структура")
		   И ПараметрыАдминистрированияИБ.Свойство("РазрешеноОткрытиеВнешнихОтчетовИОбработок") Тогда
			
			Если Не ПараметрыАдминистрированияИБ.Свойство("ПринятоРешениеПоОткрытиюВнешнихОтчетовИОбработок")
			   И ТипЗнч(ПараметрыАдминистрированияИБ.РазрешеноОткрытиеВнешнихОтчетовИОбработок) = Тип("Булево")
			   И ПараметрыАдминистрированияИБ.РазрешеноОткрытиеВнешнихОтчетовИОбработок Тогда
				
				ПараметрыАдминистрированияИБ.Вставить("ПринятоРешениеПоОткрытиюВнешнихОтчетовИОбработок", Истина);
			КонецЕсли;
			ПараметрыАдминистрированияИБ.Удалить("РазрешеноОткрытиеВнешнихОтчетовИОбработок");
			Константы.ПараметрыАдминистрированияИБ.Установить(Новый ХранилищеЗначения(ПараметрыАдминистрированияИБ));
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Вызывается при переходе на версию конфигурации 3.0.2.124 и при начальном заполнении.
Процедура ЗаполнитьНаименованиеПредопределенныхГруппПользователей() Экспорт
	
	УстановитьНаименование(Справочники.ГруппыПользователей.ВсеПользователи,
		НСтр("ru = 'Все пользователи'"));
	
	УстановитьНаименование(Справочники.ГруппыВнешнихПользователей.ВсеВнешниеПользователи,
		НСтр("ru = 'Все внешние пользователи'"));
	
КонецПроцедуры

// Для процедуры ЗаполнитьНаименованиеПредопределенныхГруппПользователей.
Процедура УстановитьНаименование(Ссылка, Наименование)
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить(Ссылка.Метаданные().ПолноеИмя());
	ЭлементБлокировки.УстановитьЗначение("Ссылка", Ссылка);
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		Объект = Ссылка.ПолучитьОбъект();
		Объект.Наименование = НСтр("ru = 'Все пользователи'");
		ОбновлениеИнформационнойБазы.ЗаписатьОбъект(Объект);
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Работа с настройками пользователей.

// Вызывается из обработки НастройкиПользователей и формирует
// список настроек пользователей.
//
Процедура ЗаполнитьСпискиНастроек(Параметры, АдресХранилища) Экспорт
	
	Если Параметры.ПользовательИнформационнойБазы <> ИмяПользователя()
	   И Не ПравоДоступа("АдминистрированиеДанных", Метаданные) Тогда
		
		ТекстОшибки = НСтр("ru = 'Недостаточно прав для получения настроек пользователя.'");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Обработки.НастройкиПользователей.ЗаполнитьСпискиНастроек(Параметры);
	
	Результат = Новый Структура;
	Результат.Вставить("НастройкиВнешнегоВида");
	Результат.Вставить("НастройкиОтчетовДерево");
	Результат.Вставить("ПрочиеНастройкиДерево");
	Результат.Вставить("ПользовательскиеВариантыОтчетов");
	
	ЗаполнитьЗначенияСвойств(Результат, Параметры);
	ПоместитьВоВременноеХранилище(Результат, АдресХранилища);
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Прочие пользовательские настройки.

// См. ПользователиПереопределяемый.ПриПолученииПрочихНастроек
Процедура ПриПолученииПрочихНастроекПользователя(СведенияОПользователе, Настройки) Экспорт
	
	ИнтеграцияПодсистемБСП.ПриПолученииПрочихНастроек(СведенияОПользователе, Настройки);
	ПользователиПереопределяемый.ПриПолученииПрочихНастроек(СведенияОПользователе, Настройки);
	
КонецПроцедуры

Процедура ПриСохраненииПрочихНастроекПользователя(СведенияОПользователе, Настройки) Экспорт
	
	ИнтеграцияПодсистемБСП.ПриСохраненииПрочихНастроек(СведенияОПользователе, Настройки);
	ПользователиПереопределяемый.ПриСохраненииПрочихНастроек(СведенияОПользователе, Настройки);
	
КонецПроцедуры

Процедура ПриУдаленииПрочихНастроекПользователя(СведенияОПользователе, Настройки) Экспорт
	
	ИнтеграцияПодсистемБСП.ПриУдаленииПрочихНастроек(СведенияОПользователе, Настройки);
	ПользователиПереопределяемый.ПриУдаленииПрочихНастроек(СведенияОПользователе, Настройки);
	
КонецПроцедуры

// Возвращаемое значение:
//  ТаблицаЗначений:
//   * Объект - Строка
//   * Идентификатор - Строка
//
Функция НовоеОписаниеНастройки() Экспорт
	
	Возврат Новый Структура;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// ВСПОМОГАТЕЛЬНЫЕ ПРОЦЕДУРЫ И ФУНКЦИИ

// При первом запуске подчиненного узла очищает идентификаторы
// пользователей ИБ, скопированные при создании начального образа.
//
Процедура ОчиститьИдентификаторыНесуществующихПользователейИБ()
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		Возврат;
	КонецЕсли;
	
	ПустойУникальныйИдентификатор = ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор();
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ПустойУникальныйИдентификатор", ПустойУникальныйИдентификатор);
	
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Пользователи.Ссылка КАК Ссылка,
	|	Пользователи.ИдентификаторПользователяИБ
	|ИЗ
	|	Справочник.Пользователи КАК Пользователи
	|ГДЕ
	|	Пользователи.ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ
	|	ВнешниеПользователи.Ссылка,
	|	ВнешниеПользователи.ИдентификаторПользователяИБ
	|ИЗ
	|	Справочник.Пользователи КАК ВнешниеПользователи
	|ГДЕ
	|	ВнешниеПользователи.ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор";
	
	Выборка = Запрос.Выполнить().Выбрать();
	
	Пока Выборка.Следующий() Цикл
		
		ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
			Выборка.ИдентификаторПользователяИБ);
		
		Если ПользовательИБ <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить(Выборка.Ссылка.Метаданные().ПолноеИмя());
		ЭлементБлокировки.УстановитьЗначение("Ссылка", Выборка.Ссылка);
		
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			ТекущийОбъект = Выборка.Ссылка.ПолучитьОбъект();
			ТекущийОбъект.ИдентификаторПользователяИБ = ПустойУникальныйИдентификатор;
			ОбновлениеИнформационнойБазы.ЗаписатьДанные(ТекущийОбъект);
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
	КонецЦикла;
	
КонецПроцедуры

// Обновляет представление внешнего пользователя при изменении представления его объекта авторизации,
// а также помечает, как недействительного, если объект авторизации помечен на удаление.
//
Процедура ОбновитьВнешнегоПользователя(СсылкаНаОбъектАвторизации, ПометкаУдаленияОбъектаАвторизации)
	
	УстановитьПривилегированныйРежим(Истина);
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ВнешниеПользователи.Ссылка КАК Ссылка,
	|	ВнешниеПользователи.Наименование КАК Наименование,
	|	ВнешниеПользователи.Недействителен КАК Недействителен,
	|	ВнешниеПользователи.ИдентификаторПользователяИБ КАК ИдентификаторПользователяИБ
	|ИЗ
	|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
	|ГДЕ
	|	ВнешниеПользователи.ОбъектАвторизации = &СсылкаНаОбъектАвторизации
	|	И (ВнешниеПользователи.Недействителен = ЛОЖЬ
	|			ИЛИ &ПометкаУдаленияОбъектаАвторизации)");
	
	Запрос.УстановитьПараметр("СсылкаНаОбъектАвторизации",            СсылкаНаОбъектАвторизации);
	Запрос.УстановитьПараметр("ПометкаУдаленияОбъектаАвторизации",    ПометкаУдаленияОбъектаАвторизации);
	
	НачатьТранзакцию();
	Попытка
		РезультатЗапроса = Запрос.Выполнить();
		
		Если Не РезультатЗапроса.Пустой() Тогда
			Выборка = РезультатЗапроса.Выбрать();
			Выборка.Следующий();
			
			Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.КонтактнаяИнформация") Тогда
				
				Если ЗначениеЗаполнено(Выборка.ИдентификаторПользователяИБ) Тогда
					
					ПользователяИБ = Пользователи.СвойстваПользователяИБ(Выборка.ИдентификаторПользователяИБ);
					Если ПользователяИБ <> Неопределено Тогда
						
						МодульУправлениеКонтактнойИнформацией = ОбщегоНазначения.ОбщийМодуль("УправлениеКонтактнойИнформацией");
						
						АдресЭлектроннойПочты = МодульУправлениеКонтактнойИнформацией.НовыйАдресПочтыДляВосстановленияПароля(
							СсылкаНаОбъектАвторизации, ПользователяИБ.АдресЭлектроннойПочты);
						Если АдресЭлектроннойПочты <> Неопределено Тогда
							ПользователяИБ.АдресЭлектроннойПочты = АдресЭлектроннойПочты;
							Пользователи.УстановитьСвойстваПользователяИБ(Выборка.ИдентификаторПользователяИБ, ПользователяИБ);
						КонецЕсли;
					КонецЕсли;
					
				КонецЕсли;
				
			КонецЕсли;
			
			Если Строка(СсылкаНаОбъектАвторизации) <> Выборка.Наименование Тогда
			
				Блокировка = Новый БлокировкаДанных;
				ЭлементБлокировки = Блокировка.Добавить("Справочник.ВнешниеПользователи");
				ЭлементБлокировки.УстановитьЗначение("Ссылка", Выборка.Ссылка);
				Блокировка.Заблокировать();
				
				ВнешнийПользовательОбъект = Выборка.Ссылка.ПолучитьОбъект();
				ВнешнийПользовательОбъект.Наименование = Строка(СсылкаНаОбъектАвторизации);
				Если ПометкаУдаленияОбъектаАвторизации И ВнешнийПользовательОбъект.Недействителен = Ложь Тогда
					ВнешнийПользовательОбъект.Недействителен = Истина;
					Если Пользователи.ВходВПрограммуРазрешен(ВнешнийПользовательОбъект.ИдентификаторПользователяИБ) Тогда
						ВнешнийПользовательОбъект.ДополнительныеСвойства.Вставить("ОписаниеПользователяИБ",
							Новый Структура("Действие, ВходВПрограммуРазрешен", "Записать", Ложь));
					КонецЕсли;
				КонецЕсли;
				ВнешнийПользовательОбъект.Записать();
			КонецЕсли;
		
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

Функция ШаблонТекстаОшибкиЗаписиСведенийОТекущемПользователе()
	
	Возврат
		НСтр("ru = 'Не удалось записать сведения о текущем пользователе по причине:
		           |%1
		           |
		           |Обратитесь к администратору.'");
	
КонецФункции

Функция ЗаголовокСообщенияАвторизацияНеВыполненаСПереводомСтроки()
	
	Возврат НСтр("ru = 'Авторизация не выполнена. Работа системы будет завершена.'")
		+ Символы.ПС + Символы.ПС;
	
КонецФункции

Функция ЗначенияПараметровСеансаТекущийПользователь()
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат ТекстСообщенияТекущийПользовательНедоступенВСеансеБезРазделителей();
	КонецЕсли;
	
	ЗаголовокОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Не удалось установить параметр сеанса %1.'"),
		"ТекущийПользователь") + Символы.ПС;
	
	НачатьТранзакцию();
	Попытка
		СведенияОПользователе = НайтиТекущегоПользователяВСправочнике();
		
		Если СведенияОПользователе.СоздатьПользователя Тогда
			СоздатьТекущегоПользователяВСправочнике(СведенияОПользователе);
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Если Не СведенияОПользователе.СоздатьПользователя
	   И Не СведенияОПользователе.ПользовательНайден Тогда
		
		Возврат ЗаголовокОшибки + ТекстСообщенияПользовательНеНайденВСправочнике(
			СведенияОПользователе.ИмяПользователя);
	КонецЕсли;
	
	Если СведенияОПользователе.ТекущийПользователь        = Неопределено
	 Или СведенияОПользователе.ТекущийВнешнийПользователь = Неопределено Тогда
		
		Возврат ЗаголовокОшибки + ТекстСообщенияПользовательНеНайденВСправочнике(
				СведенияОПользователе.ИмяПользователя) + Символы.ПС
			+ НСтр("ru = 'Возникла внутренняя ошибка при поиске пользователя.'");
	КонецЕсли;
	
	Значения = Новый Структура;
	Значения.Вставить("ТекущийПользователь",        СведенияОПользователе.ТекущийПользователь);
	Значения.Вставить("ТекущийВнешнийПользователь", СведенияОПользователе.ТекущийВнешнийПользователь);
	
	Возврат Значения;
	
КонецФункции

Функция ТекстСообщенияТекущийПользовательНедоступенВСеансеБезРазделителей()
	
	Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Недопустимое получение параметра сеанса %1
		           |в сеансе без указания всех разделителей.'"),
		"ТекущийПользователь");
	
КонецФункции

Функция НайтиТекущегоПользователяВСправочнике()
	
	Результат = Новый Структура;
	Результат.Вставить("ИмяПользователя",             Неопределено);
	Результат.Вставить("ПолноеИмяПользователя",       Неопределено);
	Результат.Вставить("ИдентификаторПользователяИБ", Неопределено);
	Результат.Вставить("ПользовательНайден",          Ложь);
	Результат.Вставить("СоздатьПользователя",         Ложь);
	Результат.Вставить("СсылкаНового",                Неопределено);
	Результат.Вставить("Служебный",                   Ложь);
	Результат.Вставить("ТекущийПользователь",         Неопределено);
	Результат.Вставить("ТекущийВнешнийПользователь",  Справочники.ВнешниеПользователи.ПустаяСсылка());
	
	ТекущийПользовательИБ = ПользователиИнформационнойБазы.ТекущийПользователь();
	
	Если ПустаяСтрока(ТекущийПользовательИБ.Имя) Тогда
		СвойстваНеуказанногоПользователя = СвойстваНеуказанногоПользователя();
		
		Результат.ИмяПользователя       = СвойстваНеуказанногоПользователя.ПолноеИмя;
		Результат.ПолноеИмяПользователя = СвойстваНеуказанногоПользователя.ПолноеИмя;
		Результат.СсылкаНового          = СвойстваНеуказанногоПользователя.СтандартнаяСсылка;
		
		Если СвойстваНеуказанногоПользователя.Ссылка = Неопределено Тогда
			Результат.СоздатьПользователя = Истина;
			Результат.Служебный = Истина;
			Результат.ИдентификаторПользователяИБ = "";
		Иначе
			Результат.ПользовательНайден = Истина;
			Результат.ТекущийПользователь = СвойстваНеуказанногоПользователя.Ссылка;
		КонецЕсли;
		
		Возврат Результат;
	КонецЕсли;

	Результат.ИмяПользователя             = ТекущийПользовательИБ.Имя;
	Результат.ИдентификаторПользователяИБ = ТекущийПользовательИБ.УникальныйИдентификатор;
	
	Пользователи.НайтиНеоднозначныхПользователейИБ(Неопределено, Результат.ИдентификаторПользователяИБ);
	
	Запрос = Новый Запрос;
	Запрос.Параметры.Вставить("ИдентификаторПользователяИБ", Результат.ИдентификаторПользователяИБ);
	
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ВнешниеПользователи.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
	|ГДЕ
	|	ВнешниеПользователи.ИдентификаторПользователяИБ = &ИдентификаторПользователяИБ";
	Выборка = Запрос.Выполнить().Выбрать();
	
	Если Выборка.Следующий() Тогда
		
		Если Не ВнешниеПользователи.ИспользоватьВнешнихПользователей() Тогда
			ВызватьИсключение НСтр("ru = 'Внешние пользователи отключены.'");
		КонецЕсли;
		
		Результат.ТекущийПользователь        = Справочники.Пользователи.ПустаяСсылка();
		Результат.ТекущийВнешнийПользователь = Выборка.Ссылка;
		
		Результат.ПользовательНайден = Истина;
		Возврат Результат;
	КонецЕсли;

	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	Пользователи.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.Пользователи КАК Пользователи
	|ГДЕ
	|	Пользователи.ИдентификаторПользователяИБ = &ИдентификаторПользователяИБ";
	Выборка = Запрос.Выполнить().Выбрать();
	
	Если Выборка.Следующий() Тогда
		Результат.ТекущийПользователь = Выборка.Ссылка;
		Результат.ПользовательНайден = Истина;
		Возврат Результат;
	КонецЕсли;
	
	ИнтеграцияПодсистемБСП.ПриОтсутствииТекущегоПользователяВСправочнике(
		Результат.СоздатьПользователя);
	
	Если Не Результат.СоздатьПользователя
	   И Не РолиАдминистратораДоступны() Тогда
		
		Возврат Результат;
	КонецЕсли;
	
	Результат.ИдентификаторПользователяИБ = ТекущийПользовательИБ.УникальныйИдентификатор;
	Результат.ПолноеИмяПользователя       = ТекущийПользовательИБ.ПолноеИмя;
	
	Если Результат.СоздатьПользователя Тогда
		Возврат Результат;
	КонецЕсли;
	
	ПользовательПоНаименованию = СсылкаПользователяПоПолномуНаименованию(
		Результат.ПолноеИмяПользователя);
	
	Если ПользовательПоНаименованию <> Неопределено Тогда
		Результат.ПользовательНайден  = Истина;
		Результат.ТекущийПользователь = ПользовательПоНаименованию;
	Иначе
		Результат.СоздатьПользователя = Истина;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

Процедура СоздатьТекущегоПользователяВСправочнике(СведенияОПользователе)
	
	НачатьТранзакцию();
	Попытка
		Если СведенияОПользователе.СсылкаНового = Неопределено Тогда
			СведенияОПользователе.СсылкаНового = Справочники.Пользователи.ПолучитьСсылку();
		КонецЕсли;
		
		СведенияОПользователе.ТекущийПользователь = СведенияОПользователе.СсылкаНового;
		
		ПараметрыСеанса.ТекущийПользователь        = СведенияОПользователе.ТекущийПользователь;
		ПараметрыСеанса.ТекущийВнешнийПользователь = СведенияОПользователе.ТекущийВнешнийПользователь;
		ПараметрыСеанса.АвторизованныйПользователь = СведенияОПользователе.ТекущийПользователь;
		
		НовыйПользователь = Справочники.Пользователи.СоздатьЭлемент();
		НовыйПользователь.Служебный    = СведенияОПользователе.Служебный;
		НовыйПользователь.Наименование = СведенияОПользователе.ПолноеИмяПользователя;
		НовыйПользователь.УстановитьСсылкуНового(СведенияОПользователе.СсылкаНового);
		
		Если ЗначениеЗаполнено(СведенияОПользователе.ИдентификаторПользователяИБ) Тогда
			
			ОписаниеПользователяИБ = Новый Структура;
			ОписаниеПользователяИБ.Вставить("Действие", "Записать");
			ОписаниеПользователяИБ.Вставить("УникальныйИдентификатор",
				СведенияОПользователе.ИдентификаторПользователяИБ);
			
			НовыйПользователь.ДополнительныеСвойства.Вставить(
				"ОписаниеПользователяИБ", ОписаниеПользователяИБ);
		КонецЕсли;
		
		ИнтеграцияПодсистемБСП.ПриАвтоматическомСозданииТекущегоПользователяВСправочнике(
			НовыйПользователь);
		
		НовыйПользователь.Записать();
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ОчищаемыеПараметры = Новый Массив;
		ОчищаемыеПараметры.Добавить("ТекущийПользователь");
		ОчищаемыеПараметры.Добавить("ТекущийВнешнийПользователь");
		ОчищаемыеПараметры.Добавить("АвторизованныйПользователь");
		ПараметрыСеанса.Очистить(ОчищаемыеПараметры);
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

Функция ТекстСообщенияПользовательНеНайденВСправочнике(ИмяПользователя)
	
	Если ВнешниеПользователи.ИспользоватьВнешнихПользователей() Тогда
		ШаблонСообщенияОбОшибке =
			НСтр("ru = 'Пользователь ""%1"" не существует в справочниках
			           |""Пользователи"" и ""Внешние пользователи"".
			           |
			           |Обратитесь к администратору.'");
	Иначе
		ШаблонСообщенияОбОшибке =
			НСтр("ru = 'Пользователь ""%1"" не существует в справочнике ""Пользователи"".
			           |
			           |Обратитесь к администратору.'");
	КонецЕсли;
	
	Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонСообщенияОбОшибке, ИмяПользователя);
	
КонецФункции

Функция СсылкаПользователяПоПолномуНаименованию(ПолноеИмя)
	
	УстановитьПривилегированныйРежим(Истина);
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Пользователи.Ссылка КАК Ссылка,
	|	Пользователи.ИдентификаторПользователяИБ
	|ИЗ
	|	Справочник.Пользователи КАК Пользователи
	|ГДЕ
	|	Пользователи.Наименование = &ПолноеИмя";
	
	Запрос.УстановитьПараметр("ПолноеИмя", ПолноеИмя);
	
	Результат = Неопределено;
	
	НачатьТранзакцию();
	Попытка
		РезультатЗапроса = Запрос.Выполнить();
		Если НЕ РезультатЗапроса.Пустой() Тогда
			
			Выборка = РезультатЗапроса.Выбрать();
			Выборка.Следующий();
			
			Если НЕ Пользователи.ПользовательИБЗанят(Выборка.ИдентификаторПользователяИБ) Тогда
				Результат = Выборка.Ссылка;
			КонецЕсли;
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Возврат Результат;
	
КонецФункции

Функция РезультатУстановкиПараметровСеанса(РегистрироватьВЖурнале)
	
	Попытка
		Пользователи.АвторизованныйПользователь();
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		Возврат КраткоеПредставлениеОшибкиАвторизацииПослеРегистрацииВЖурнале(ИнформацияОбОшибке,
			, РегистрироватьВЖурнале);
	КонецПопытки;
	
	Возврат "";
	
КонецФункции

Функция КраткоеПредставлениеОшибкиАвторизацииПослеРегистрацииВЖурнале(ИнформацияОбОшибке, ШаблонОшибки = "", РегистрироватьВЖурнале = Истина)
	
	Если ТипЗнч(ИнформацияОбОшибке) = Тип("ИнформацияОбОшибке") Тогда
		КраткоеПредставление   = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке);
		ПодробноеПредставление = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
	Иначе
		КраткоеПредставление   = ИнформацияОбОшибке;
		ПодробноеПредставление = ИнформацияОбОшибке;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ШаблонОшибки) Тогда
		КраткоеПредставление = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			ШаблонОшибки, ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
		
		ПодробноеПредставление = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			ШаблонОшибки, ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
	КонецЕсли;
	
	КраткоеПредставление   = ЗаголовокСообщенияАвторизацияНеВыполненаСПереводомСтроки() + КраткоеПредставление;
	ПодробноеПредставление = ЗаголовокСообщенияАвторизацияНеВыполненаСПереводомСтроки() + ПодробноеПредставление;
	
	Если РегистрироватьВЖурнале Тогда
		ЗаписьЖурналаРегистрации(
			ИмяСобытияОшибкаВходаВПрограммуДляЖурналаРегистрации(),
			УровеньЖурналаРегистрации.Ошибка, , , ПодробноеПредставление);
	КонецЕсли;
	
	Возврат КраткоеПредставление;
	
КонецФункции

Функция ИмяСобытияОшибкаВходаВПрограммуДляЖурналаРегистрации()
	
	Возврат НСтр("ru = 'Пользователи.Ошибка входа в программу'", ОбщегоНазначения.КодОсновногоЯзыка());
	
КонецФункции

// Используется в процедурах ОбновитьСоставыГруппПользователей,
// ОбновитьСоставыГруппВнешнихПользователей.
//
// Параметры:
//  Таблица - Строка - полное имя объекта метаданных.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//   * Ссылка - СправочникСсылка.ГруппыВнешнихПользователей
//            - СправочникСсылка.ГруппыПользователей - ссылка на элемент справочника.
//   * Родитель - СправочникСсылка.ГруппыВнешнихПользователей
//              - СправочникСсылка.ГруппыПользователей - ссылка на родителя
//       элемента справочника.
//
Функция СсылкиВИерархииРодителей(Таблица)
	
	СсылкиРодителей = СсылкиРодителей(Таблица);
	СсылкиВИерархииРодителей = СсылкиРодителей.Скопировать(Новый Массив);
	
	Для каждого ОписаниеСсылки Из СсылкиРодителей Цикл
		НоваяСтрока = СсылкиВИерархииРодителей.Добавить();
		НоваяСтрока.Родитель = ОписаниеСсылки.Ссылка;
		НоваяСтрока.Ссылка   = ОписаниеСсылки.Ссылка;
		
		ЗаполнитьСсылкиВИерархииРодителя(ОписаниеСсылки.Ссылка, ОписаниеСсылки.Ссылка, СсылкиРодителей, СсылкиВИерархииРодителей);
	КонецЦикла;
	
	Возврат СсылкиВИерархииРодителей;
	
КонецФункции

// Подготовка состава групп родителей.
//
// Параметры:
//  Таблица - Строка - полное имя объекта метаданных.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//    * Ссылка - СправочникСсылка.ГруппыВнешнихПользователей
//             - СправочникСсылка.ГруппыПользователей
//    * Родитель - СправочникСсылка.ГруппыВнешнихПользователей
//               - СправочникСсылка.ГруппыПользователей
//
Функция СсылкиРодителей(Таблица)
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ
	|	СсылкиРодителей.Ссылка КАК Ссылка,
	|	СсылкиРодителей.Родитель КАК Родитель
	|ИЗ
	|	&Таблица КАК СсылкиРодителей");
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&Таблица", Таблица);
	
	СсылкиРодителей = Запрос.Выполнить().Выгрузить();
	СсылкиРодителей.Индексы.Добавить("Родитель");
	
	Возврат СсылкиРодителей;
	
КонецФункции

Процедура ЗаполнитьСсылкиВИерархииРодителя(Знач Родитель, Знач ТекущийРодитель, Знач СсылкиРодителей, Знач СсылкиВИерархииРодителей)
	
	СсылкиРодителя = СсылкиРодителей.НайтиСтроки(Новый Структура("Родитель", ТекущийРодитель));
	
	Для каждого ОписаниеСсылки Из СсылкиРодителя Цикл
		НоваяСтрока = СсылкиВИерархииРодителей.Добавить();
		НоваяСтрока.Родитель = Родитель;
		НоваяСтрока.Ссылка   = ОписаниеСсылки.Ссылка;
		
		ЗаполнитьСсылкиВИерархииРодителя(Родитель, ОписаниеСсылки.Ссылка, СсылкиРодителей, СсылкиВИерархииРодителей);
	КонецЦикла;
	
КонецПроцедуры

// Используется в процедурах ОбновитьСоставыГруппПользователей,
// ОбновитьСоставыГруппВнешнихПользователей.
//
Процедура ОбновитьСоставГруппыВсеПользователи(Пользователь,
                                              ОбновитьГруппуВнешнихПользователей = Ложь,
                                              УчастникиИзменений = Неопределено,
                                              ИзмененныеГруппы   = Неопределено)
	
	Если ОбновитьГруппуВнешнихПользователей Тогда
		ГруппаВсеПользователи = Справочники.ГруппыВнешнихПользователей.ВсеВнешниеПользователи;
	Иначе
		ГруппаВсеПользователи = Справочники.ГруппыПользователей.ВсеПользователи;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ГруппаВсеПользователи", ГруппаВсеПользователи);
	
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Пользователи.Ссылка КАК Ссылка,
	|	ВЫБОР
	|		КОГДА Пользователи.ПометкаУдаления
	|			ТОГДА ЛОЖЬ
	|		КОГДА Пользователи.Недействителен
	|			ТОГДА ЛОЖЬ
	|		ИНАЧЕ ИСТИНА
	|	КОНЕЦ КАК Используется
	|ПОМЕСТИТЬ Пользователи
	|ИЗ
	|	Справочник.Пользователи КАК Пользователи
	|ГДЕ
	|	&ОтборПользователя
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Пользователи.Ссылка
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	&ГруппаВсеПользователи КАК ГруппаПользователей,
	|	Пользователи.Ссылка КАК Пользователь,
	|	Пользователи.Используется
	|ИЗ
	|	Пользователи КАК Пользователи
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|		ПО (СоставыГруппПользователей.ГруппаПользователей = &ГруппаВсеПользователи)
	|			И (СоставыГруппПользователей.Пользователь = Пользователи.Ссылка)
	|			И (СоставыГруппПользователей.Используется = Пользователи.Используется)
	|ГДЕ
	|	СоставыГруппПользователей.Пользователь ЕСТЬ NULL 
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ
	|	Пользователи.Ссылка,
	|	Пользователи.Ссылка,
	|	Пользователи.Используется
	|ИЗ
	|	Пользователи КАК Пользователи
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|		ПО (СоставыГруппПользователей.ГруппаПользователей = Пользователи.Ссылка)
	|			И (СоставыГруппПользователей.Пользователь = Пользователи.Ссылка)
	|			И (СоставыГруппПользователей.Используется = Пользователи.Используется)
	|ГДЕ
	|	СоставыГруппПользователей.Пользователь ЕСТЬ NULL ";
	
	Блокировка = Новый БлокировкаДанных;
	
	Если ОбновитьГруппуВнешнихПользователей Тогда
		ПолноеИмяСправочника = "Справочник.ВнешниеПользователи";
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "Справочник.Пользователи", "Справочник.ВнешниеПользователи");
	Иначе
		ПолноеИмяСправочника = "Справочник.Пользователи";
	КонецЕсли;
	
	Если ТипЗнч(Пользователь) = Тип("Массив") И Пользователь.Количество() <= 1000 Тогда
		Для Каждого Ссылка Из Пользователь Цикл
			ЭлементБлокировкиРегистра = Блокировка.Добавить("РегистрСведений.СоставыГруппПользователей");
			ЭлементБлокировкиРегистра.УстановитьЗначение("ГруппаПользователей", ГруппаВсеПользователи);
			ЭлементБлокировкиРегистра.УстановитьЗначение("Пользователь", Ссылка);
			ЭлементБлокировкиСправочника = Блокировка.Добавить(ПолноеИмяСправочника);
			ЭлементБлокировкиСправочника.Режим = РежимБлокировкиДанных.Разделяемый;
			ЭлементБлокировкиСправочника.УстановитьЗначение("Ссылка", Ссылка);
		КонецЦикла;
	Иначе
		ЭлементБлокировкиРегистра = Блокировка.Добавить("РегистрСведений.СоставыГруппПользователей");
		ЭлементБлокировкиСправочника = Блокировка.Добавить(ПолноеИмяСправочника);
		ЭлементБлокировкиСправочника.Режим = РежимБлокировкиДанных.Разделяемый;
		Если Пользователь <> Неопределено И ТипЗнч(Пользователь) <> Тип("Массив") Тогда
			ЭлементБлокировкиРегистра.УстановитьЗначение("ГруппаПользователей", ГруппаВсеПользователи);
			ЭлементБлокировкиРегистра.УстановитьЗначение("Пользователь", Пользователь);
			ЭлементБлокировкиСправочника.УстановитьЗначение("Ссылка", Пользователь);
		КонецЕсли;
	КонецЕсли;
	
	Если Пользователь = Неопределено Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОтборПользователя", "ИСТИНА");
	Иначе
		Запрос.УстановитьПараметр("Пользователь", Пользователь);
		Запрос.Текст = СтрЗаменить(
			Запрос.Текст, "&ОтборПользователя", "Пользователи.Ссылка В (&Пользователь)");
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		РезультатЗапроса = Запрос.Выполнить();
		
		Если Не РезультатЗапроса.Пустой() Тогда
			НаборЗаписей = РегистрыСведений.СоставыГруппПользователей.СоздатьНаборЗаписей();
			Запись = НаборЗаписей.Добавить();
			Выборка = РезультатЗапроса.Выбрать();
			
			Пока Выборка.Следующий() Цикл
				НаборЗаписей.Отбор.ГруппаПользователей.Установить(Выборка.ГруппаПользователей);
				НаборЗаписей.Отбор.Пользователь.Установить(Выборка.Пользователь);
				ЗаполнитьЗначенияСвойств(Запись, Выборка);
				НаборЗаписей.Записать(); // Добавление недостающих записей о связях.
				
				Если УчастникиИзменений <> Неопределено Тогда
					УчастникиИзменений.Вставить(Выборка.Пользователь);
				КонецЕсли;
			КонецЦикла;
			
			Если ИзмененныеГруппы <> Неопределено Тогда
				ИзмененныеГруппы.Вставить(ГруппаВсеПользователи);
			КонецЕсли;
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Используется в процедуре ОбновитьСоставыГруппВнешнихПользователей.
Процедура ОбновитьСоставыГруппПоТипамОбъектовАвторизации(ГруппаВнешнихПользователей,
		ВнешнийПользователь, УчастникиИзменений, ИзмененныеГруппы)
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ГруппыВнешнихПользователей.Ссылка КАК ГруппаПользователей,
	|	ВнешниеПользователи.Ссылка КАК Пользователь,
	|	ВЫБОР
	|		КОГДА ГруппыВнешнихПользователей.ПометкаУдаления
	|			ТОГДА ЛОЖЬ
	|		КОГДА ВнешниеПользователи.ПометкаУдаления
	|			ТОГДА ЛОЖЬ
	|		КОГДА ВнешниеПользователи.Недействителен
	|			ТОГДА ЛОЖЬ
	|		ИНАЧЕ ИСТИНА
	|	КОНЕЦ КАК Используется
	|ПОМЕСТИТЬ НовыеСоставы
	|ИЗ
	|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыВнешнихПользователей КАК ГруппыВнешнихПользователей
	|		ПО (ГруппыВнешнихПользователей.ВсеОбъектыАвторизации = ИСТИНА)
	|			И (&ОтборГруппыВнешнихПользователей1)
	|			И (ИСТИНА В
	|				(ВЫБРАТЬ ПЕРВЫЕ 1
	|					ИСТИНА
	|				ИЗ
	|					Справочник.ГруппыВнешнихПользователей.Назначение КАК ТипыПользователей
	|				ГДЕ
	|					ТипыПользователей.Ссылка = ГруппыВнешнихПользователей.Ссылка
	|					И ТИПЗНАЧЕНИЯ(ТипыПользователей.ТипПользователей) = ТИПЗНАЧЕНИЯ(ВнешниеПользователи.ОбъектАвторизации)))
	|			И (&ОтборВнешнегоПользователя1)
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	СоставыГруппПользователей.ГруппаПользователей,
	|	СоставыГруппПользователей.Пользователь
	|ИЗ
	|	РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|		ЛЕВОЕ СОЕДИНЕНИЕ НовыеСоставы КАК НовыеСоставы
	|		ПО СоставыГруппПользователей.ГруппаПользователей = НовыеСоставы.ГруппаПользователей
	|			И СоставыГруппПользователей.Пользователь = НовыеСоставы.Пользователь
	|ГДЕ
	|	ТИПЗНАЧЕНИЯ(СоставыГруппПользователей.ГруппаПользователей) = ТИП(Справочник.ГруппыВнешнихПользователей)
	|	И ВЫРАЗИТЬ(СоставыГруппПользователей.ГруппаПользователей КАК Справочник.ГруппыВнешнихПользователей).ВсеОбъектыАвторизации = ИСТИНА
	|	И &ОтборГруппыВнешнихПользователей2
	|	И &ОтборВнешнегоПользователя2
	|	И НовыеСоставы.Пользователь ЕСТЬ NULL 
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	НовыеСоставы.ГруппаПользователей,
	|	НовыеСоставы.Пользователь,
	|	НовыеСоставы.Используется
	|ИЗ
	|	НовыеСоставы КАК НовыеСоставы
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|		ПО (СоставыГруппПользователей.ГруппаПользователей = НовыеСоставы.ГруппаПользователей)
	|			И (СоставыГруппПользователей.Пользователь = НовыеСоставы.Пользователь)
	|			И (СоставыГруппПользователей.Используется = НовыеСоставы.Используется)
	|ГДЕ
	|	СоставыГруппПользователей.Пользователь ЕСТЬ NULL ";
	
	Если ГруппаВнешнихПользователей = Неопределено Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОтборГруппыВнешнихПользователей1", "ИСТИНА");
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОтборГруппыВнешнихПользователей2", "ИСТИНА");
	Иначе
		Запрос.УстановитьПараметр("ГруппаВнешнихПользователей", ГруппаВнешнихПользователей);
		Запрос.Текст = СтрЗаменить(
			Запрос.Текст,
			"&ОтборГруппыВнешнихПользователей1",
			"ГруппыВнешнихПользователей.Ссылка В (&ГруппаВнешнихПользователей)");
		Запрос.Текст = СтрЗаменить(
			Запрос.Текст,
			"&ОтборГруппыВнешнихПользователей2",
			"СоставыГруппПользователей.ГруппаПользователей В (&ГруппаВнешнихПользователей)");
	КонецЕсли;
	
	Если ВнешнийПользователь = Неопределено Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОтборВнешнегоПользователя1", "ИСТИНА");
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОтборВнешнегоПользователя2", "ИСТИНА");
	Иначе
		Запрос.УстановитьПараметр("ВнешнийПользователь", ВнешнийПользователь);
		Запрос.Текст = СтрЗаменить(
			Запрос.Текст,
			"&ОтборВнешнегоПользователя1",
			"ВнешниеПользователи.Ссылка В (&ВнешнийПользователь)");
		Запрос.Текст = СтрЗаменить(
			Запрос.Текст,
			"&ОтборВнешнегоПользователя2",
			"СоставыГруппПользователей.Пользователь В (&ВнешнийПользователь)");
	КонецЕсли;
	
	РезультатыЗапросов = Запрос.ВыполнитьПакет();
	
	Если НЕ РезультатыЗапросов[1].Пустой() Тогда
		НаборЗаписей = РегистрыСведений.СоставыГруппПользователей.СоздатьНаборЗаписей();
		Выборка = РезультатыЗапросов[1].Выбрать();
		
		Пока Выборка.Следующий() Цикл
			НаборЗаписей.Отбор.ГруппаПользователей.Установить(Выборка.ГруппаПользователей);
			НаборЗаписей.Отбор.Пользователь.Установить(Выборка.Пользователь);
			НаборЗаписей.Записать(); // Удаление лишних записей о связях.
			
			Если УчастникиИзменений <> Неопределено Тогда
				УчастникиИзменений.Вставить(Выборка.Пользователь);
			КонецЕсли;
			
			Если ИзмененныеГруппы <> Неопределено
			   И ТипЗнч(Выборка.ГруппаПользователей)
			     = Тип("СправочникСсылка.ГруппыВнешнихПользователей") Тогда
				
				ИзмененныеГруппы.Вставить(Выборка.ГруппаПользователей);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Если НЕ РезультатыЗапросов[2].Пустой() Тогда
		НаборЗаписей = РегистрыСведений.СоставыГруппПользователей.СоздатьНаборЗаписей();
		Запись = НаборЗаписей.Добавить();
		Выборка = РезультатыЗапросов[2].Выбрать();
		
		Пока Выборка.Следующий() Цикл
			НаборЗаписей.Отбор.ГруппаПользователей.Установить(Выборка.ГруппаПользователей);
			НаборЗаписей.Отбор.Пользователь.Установить(Выборка.Пользователь);
			ЗаполнитьЗначенияСвойств(Запись, Выборка);
			НаборЗаписей.Записать(); // Добавление недостающих записей о связях.
			
			Если УчастникиИзменений <> Неопределено Тогда
				УчастникиИзменений.Вставить(Выборка.Пользователь);
			КонецЕсли;
			
			Если ИзмененныеГруппы <> Неопределено
			   И ТипЗнч(Выборка.ГруппаПользователей)
			     = Тип("СправочникСсылка.ГруппыВнешнихПользователей") Тогда
				
				ИзмененныеГруппы.Вставить(Выборка.ГруппаПользователей);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

// Используется в процедурах ОбновитьСоставыГруппПользователей,
// ОбновитьСоставыГруппВнешнихПользователей.
//
Процедура ОбновитьСоставыИерархическихГруппПользователей(ГруппаПользователей,
                                                         Пользователь,
                                                         УчастникиИзменений = Неопределено,
                                                         ИзмененныеГруппы   = Неопределено)
	
	ОбновитьГруппыВнешнихПользователей =
		ТипЗнч(ГруппаПользователей) <> Тип("СправочникСсылка.ГруппыПользователей");
	
	// Подготовка групп пользователей в иерархии их родителей.
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	СсылкиВИерархииРодителей.Родитель,
	|	СсылкиВИерархииРодителей.Ссылка
	|ПОМЕСТИТЬ СсылкиВИерархииРодителей
	|ИЗ
	|	&СсылкиВИерархииРодителей КАК СсылкиВИерархииРодителей";
	
	Запрос.УстановитьПараметр("СсылкиВИерархииРодителей", СсылкиВИерархииРодителей(
		?(ОбновитьГруппыВнешнихПользователей,
		  "Справочник.ГруппыВнешнихПользователей",
		  "Справочник.ГруппыПользователей") ));
	
	// Подготовка запроса для цикла.
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	СоставыГруппПользователей.Пользователь,
	|	СоставыГруппПользователей.Используется
	|ПОМЕСТИТЬ СоставыГруппПользователей
	|ИЗ
	|	РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|ГДЕ
	|	&ОтборПользователяВРегистре
	|	И СоставыГруппПользователей.ГруппаПользователей = &ГруппаПользователей
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	СоставыГруппПользователей.Пользователь,
	|	СоставыГруппПользователей.Используется
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ГруппыПользователейСостав.Пользователь КАК Пользователь,
	|	МАКСИМУМ(ВЫБОР
	|			КОГДА ГруппыПользователейСостав.Ссылка.ПометкаУдаления
	|				ТОГДА ЛОЖЬ
	|			КОГДА ГруппыПользователейСостав.Пользователь.ПометкаУдаления
	|				ТОГДА ЛОЖЬ
	|			КОГДА ГруппыПользователейСостав.Пользователь.Недействителен
	|				ТОГДА ЛОЖЬ
	|			ИНАЧЕ ИСТИНА
	|		КОНЕЦ) КАК Используется
	|ПОМЕСТИТЬ НовыеСоставыГруппПользователей
	|ИЗ
	|	Справочник.ГруппыПользователей.Состав КАК ГруппыПользователейСостав
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ СсылкиВИерархииРодителей КАК СсылкиВИерархииРодителей
	|		ПО (СсылкиВИерархииРодителей.Ссылка = ГруппыПользователейСостав.Ссылка)
	|			И (СсылкиВИерархииРодителей.Родитель = &ГруппаПользователей)
	|			И (&ОтборПользователяВСправочнике)
	|
	|СГРУППИРОВАТЬ ПО
	|	ГруппыПользователейСостав.Пользователь
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Пользователь
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	СоставыГруппПользователей.Пользователь
	|ИЗ
	|	СоставыГруппПользователей КАК СоставыГруппПользователей
	|		ЛЕВОЕ СОЕДИНЕНИЕ НовыеСоставыГруппПользователей КАК НовыеСоставыГруппПользователей
	|		ПО СоставыГруппПользователей.Пользователь = НовыеСоставыГруппПользователей.Пользователь
	|ГДЕ
	|	НовыеСоставыГруппПользователей.Пользователь ЕСТЬ NULL 
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	&ГруппаПользователей КАК ГруппаПользователей,
	|	НовыеСоставыГруппПользователей.Пользователь,
	|	НовыеСоставыГруппПользователей.Используется
	|ИЗ
	|	НовыеСоставыГруппПользователей КАК НовыеСоставыГруппПользователей
	|		ЛЕВОЕ СОЕДИНЕНИЕ СоставыГруппПользователей КАК СоставыГруппПользователей
	|		ПО (СоставыГруппПользователей.Пользователь = НовыеСоставыГруппПользователей.Пользователь)
	|			И (СоставыГруппПользователей.Используется = НовыеСоставыГруппПользователей.Используется)
	|ГДЕ
	|	СоставыГруппПользователей.Пользователь ЕСТЬ NULL 
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ГруппыПользователей.Родитель КАК Родитель
	|ИЗ
	|	Справочник.ГруппыПользователей КАК ГруппыПользователей
	|ГДЕ
	|	ГруппыПользователей.Ссылка = &ГруппаПользователей
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|УНИЧТОЖИТЬ СоставыГруппПользователей
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|УНИЧТОЖИТЬ НовыеСоставыГруппПользователей";
	
	Если Пользователь = Неопределено Тогда
		ОтборПользователяВРегистре    = "ИСТИНА";
		ОтборПользователяВСправочнике = "ИСТИНА";
	Иначе
		Запрос.УстановитьПараметр("Пользователь", Пользователь);
		ОтборПользователяВРегистре    = "СоставыГруппПользователей.Пользователь В (&Пользователь)";
		ОтборПользователяВСправочнике = "ГруппыПользователейСостав.Пользователь В (&Пользователь)";
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОтборПользователяВРегистре",    ОтборПользователяВРегистре);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОтборПользователяВСправочнике", ОтборПользователяВСправочнике);
	
	Блокировка = Новый БлокировкаДанных;
	Блокировка.Добавить("РегистрСведений.СоставыГруппПользователей");
	
	Если ОбновитьГруппыВнешнихПользователей Тогда
		
		ТекстЗапроса = СтрЗаменить(
			ТекстЗапроса,
			"Справочник.ГруппыПользователей",
			"Справочник.ГруппыВнешнихПользователей");
		
		ТекстЗапроса = СтрЗаменить(
			ТекстЗапроса,
			"ГруппыПользователейСостав.Пользователь",
			"ГруппыПользователейСостав.ВнешнийПользователь");
	КонецЕсли;
	Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		Запрос.Выполнить();
		Запрос.Текст = ТекстЗапроса;
	
		// Выполнение для текущий группы пользователей и каждой группы-родителя.
		Пока ЗначениеЗаполнено(ГруппаПользователей) Цикл
			
			Запрос.УстановитьПараметр("ГруппаПользователей", ГруппаПользователей);
			
			// @skip-check query-in-loop - Обход иерархии элементов с обновлением порциями
			РезультатыЗапроса = Запрос.ВыполнитьПакет();
			
			Если НЕ РезультатыЗапроса[2].Пустой() Тогда
				НаборЗаписей = РегистрыСведений.СоставыГруппПользователей.СоздатьНаборЗаписей();
				Выборка = РезультатыЗапроса[2].Выбрать();
				
				Пока Выборка.Следующий() Цикл
					НаборЗаписей.Отбор.Пользователь.Установить(Выборка.Пользователь);
					НаборЗаписей.Отбор.ГруппаПользователей.Установить(ГруппаПользователей);
					НаборЗаписей.Записать(); // Удаление лишних записей о связях.
					
					Если УчастникиИзменений <> Неопределено Тогда
						УчастникиИзменений.Вставить(Выборка.Пользователь);
					КонецЕсли;
					
					Если ИзмененныеГруппы <> Неопределено Тогда
						ИзмененныеГруппы.Вставить(ГруппаПользователей);
					КонецЕсли;
				КонецЦикла;
			КонецЕсли;
			
			Если НЕ РезультатыЗапроса[3].Пустой() Тогда
				НаборЗаписей = РегистрыСведений.СоставыГруппПользователей.СоздатьНаборЗаписей();
				Запись = НаборЗаписей.Добавить();
				Выборка = РезультатыЗапроса[3].Выбрать();
				
				Пока Выборка.Следующий() Цикл
					НаборЗаписей.Отбор.Пользователь.Установить(Выборка.Пользователь);
					НаборЗаписей.Отбор.ГруппаПользователей.Установить(Выборка.ГруппаПользователей);
					ЗаполнитьЗначенияСвойств(Запись, Выборка);
					НаборЗаписей.Записать(); // Добавление недостающих записей о связях.
					
					Если УчастникиИзменений <> Неопределено Тогда
						УчастникиИзменений.Вставить(Выборка.Пользователь);
					КонецЕсли;
					
					Если ИзмененныеГруппы <> Неопределено Тогда
						ИзмененныеГруппы.Вставить(Выборка.ГруппаПользователей);
					КонецЕсли;
				КонецЦикла;
			КонецЕсли;
			
			Если НЕ РезультатыЗапроса[4].Пустой() Тогда
				Выборка = РезультатыЗапроса[4].Выбрать();
				Выборка.Следующий();
				ГруппаПользователей = Выборка.Родитель;
			Иначе
				ГруппаПользователей = Неопределено;
			КонецЕсли;
		КонецЦикла;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Проверяет права указанного пользователя ИБ.
//
// Параметры:
//  ПользовательИБ         - ПользовательИнформационнойБазы - проверяемый пользователь.
//  РежимПроверки          - Строка - ПриЗаписи или ПриЗапуске.
//  ЭтоВнешнийПользователь - Булево - проверять права для внешнего пользователя.
//  ВызыватьИсключение     - Булево - вызвать исключение вместо возврата текста ошибки.
//  РегистрироватьВЖурнале - Булево - записать ошибку в журнал регистрации,
//                                    когда ВызыватьИсключение указано Ложь.
//
// Возвращаемое значение:
//  Строка - текст ошибки, когда ВызыватьИсключение указано Ложь.
//
Функция ПроверитьПраваПользователя(ПользовательИБ, РежимПроверки, ЭтоВнешнийПользователь,
			ВызыватьИсключение = Истина, РегистрироватьВЖурнале = Истина)
	
	РазделениеВключено = ОбщегоНазначения.РазделениеВключено();
	Если РазделениеВключено И ПользовательИБ.РазделениеДанных.Количество() = 0 Тогда
		Возврат ""; // Не нужно проверять неразделенных пользователей в модели сервиса.
	КонецЕсли;
	
	Если Не РазделениеВключено И РежимПроверки = "ПриЗапуске" И Не ЭтоВнешнийПользователь Тогда
		Возврат ""; // Не нужно проверять права пользователей при запуске в локальном режиме.
	КонецЕсли;
	
	
	НедоступныеРоли = НедоступныеРолиПоТипуПользователя(ЭтоВнешнийПользователь);
	
	РолиДляПроверки = Новый ТаблицаЗначений;
	РолиДляПроверки.Колонки.Добавить("Роль", Новый ОписаниеТипов("ОбъектМетаданных"));
	Для Каждого Роль Из ПользовательИБ.Роли Цикл
		РолиДляПроверки.Добавить().Роль = Роль;
	КонецЦикла;
	РолиДляПроверки.Индексы.Добавить("Роль");
	
	Если Не РазделениеВключено И РежимПроверки = "ПередЗаписью" Тогда
		
		ПрежнийПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
			ПользовательИБ.УникальныйИдентификатор);
		
		Если ПрежнийПользовательИБ <> Неопределено Тогда
			Для Каждого Роль Из ПрежнийПользовательИБ.Роли Цикл
				Строка = РолиДляПроверки.Найти(Роль, "Роль");
				Если Строка <> Неопределено Тогда
					РолиДляПроверки.Удалить(Строка);
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
	КонецЕсли;
	
	ДобавляемыеНедоступныеРоли = "";
	НазначениеРолей = Неопределено;
	
	Для Каждого ОписаниеРоли Из РолиДляПроверки Цикл
		Роль = ОписаниеРоли.Роль;
		ИмяРоли = Роль.Имя;
		
		Если НедоступныеРоли.Получить(ИмяРоли) = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		Если НазначениеРолей = Неопределено Тогда
			НазначениеРолей = ПользователиСлужебныйПовтИсп.НазначениеРолей();
		КонецЕсли;
		
		Если НазначениеРолей.ТолькоДляАдминистраторовСистемы.Получить(ИмяРоли) <> Неопределено Тогда
			ТекстШаблона = НСтр("ru = '""%1"" (предназначена только для администраторов системы)'");
		
		ИначеЕсли РазделениеВключено
		        И НазначениеРолей.ТолькоДляПользователейСистемы.Получить(ИмяРоли) <> Неопределено Тогда
			
			ТекстШаблона = НСтр("ru = '""%1"" (предназначена только для пользователей системы)'");
			
		ИначеЕсли НазначениеРолей.ТолькоДляВнешнихПользователей.Получить(ИмяРоли) <> Неопределено Тогда
			ТекстШаблона = НСтр("ru = '""%1"" (предназначена только для внешних пользователей)'");
			
		Иначе // Это внешний пользователь.
			ТекстШаблона = НСтр("ru = '""%1"" (предназначена только для пользователей)'");
		КонецЕсли;
		
		ДобавляемыеНедоступныеРоли = ДобавляемыеНедоступныеРоли
			+ СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстШаблона, Роль.Представление()) + Символы.ПС;
	КонецЦикла;
	
	ДобавляемыеНедоступныеРоли = СокрЛП(ДобавляемыеНедоступныеРоли);
	
	Если Не ЗначениеЗаполнено(ДобавляемыеНедоступныеРоли) Тогда
		Возврат "";
	КонецЕсли;
	
	Если РежимПроверки = "ПриЗапуске" Тогда
		Если ВызыватьИсключение Или РегистрироватьВЖурнале Тогда
			Если СтрЧислоСтрок(ДобавляемыеНедоступныеРоли) = 1 Тогда
				ТекстРегистрацииВхода = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Попытка входа пользователя %1 с недоступной ролью:
					           |%2.'"),
				ПользовательИБ.ПолноеИмя, ДобавляемыеНедоступныеРоли);
			Иначе
				ТекстРегистрацииВхода = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Попытка входа пользователя %1 с недоступными ролями:
					           |%2.'"),
				ПользовательИБ.ПолноеИмя, ДобавляемыеНедоступныеРоли);
			КонецЕсли;
			ЗаписьЖурналаРегистрации(ИмяСобытияОшибкаВходаВПрограммуДляЖурналаРегистрации(),
				УровеньЖурналаРегистрации.Ошибка, , ПользовательИБ, ТекстРегистрацииВхода);
		КонецЕсли;
		
		ТекстСообщенияВхода =
			НСтр("ru = 'Невозможно выполнить вход из-за наличия недоступных ролей.
			           |Обратитесь к администратору.'");
		
		Если ВызыватьИсключение Тогда
			ВызватьИсключение ТекстСообщенияВхода;
		Иначе
			Возврат ТекстСообщенияВхода;
		КонецЕсли;
	КонецЕсли;
	
	Если ВызыватьИсключение Или РегистрироватьВЖурнале Тогда
		Если СтрЧислоСтрок(ДобавляемыеНедоступныеРоли) = 1 И ЗначениеЗаполнено(ДобавляемыеНедоступныеРоли) Тогда
			ТекстРегистрацииДобавления = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Попытка назначить пользователю %1 недоступную роль:
				           |%2.'"),
				ПользовательИБ.ПолноеИмя, ДобавляемыеНедоступныеРоли);
				
		ИначеЕсли СтрЧислоСтрок(ДобавляемыеНедоступныеРоли) > 1 Тогда
			ТекстРегистрацииДобавления = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Попытка назначить пользователю %1 недоступные роли:
				           |%2.'"),
				ПользовательИБ.ПолноеИмя, ДобавляемыеНедоступныеРоли);
		Иначе
			ТекстРегистрацииДобавления = "";
		КонецЕсли;
		ИмяСобытия = НСтр("ru = 'Пользователи.Ошибка при установке ролей пользователю ИБ'",
			ОбщегоНазначения.КодОсновногоЯзыка());
		
		ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка, , ПользовательИБ,
			ТекстРегистрацииДобавления);
	КонецЕсли;
	
	Если СтрЧислоСтрок(ДобавляемыеНедоступныеРоли) = 1 И ЗначениеЗаполнено(ДобавляемыеНедоступныеРоли) Тогда
		ТекстСообщенияДобавления = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Пользователю ""%1"" невозможно назначить недоступную роль:
			           |%2.'"),
			ПользовательИБ.ПолноеИмя, ДобавляемыеНедоступныеРоли);
		
	ИначеЕсли СтрЧислоСтрок(ДобавляемыеНедоступныеРоли) > 1 Тогда
		ТекстСообщенияДобавления = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Пользователю ""%1"" невозможно назначить недоступные роли:
			           |%2.'"),
			ПользовательИБ.ПолноеИмя, ДобавляемыеНедоступныеРоли);
	Иначе
		ТекстСообщенияДобавления = "";
	КонецЕсли;
	
	Если ВызыватьИсключение Тогда
		ВызватьИсключение ТекстСообщенияДобавления;
	Иначе
		Возврат ТекстСообщенияДобавления;
	КонецЕсли;
	
КонецФункции

Функция СписокНастроек(ИмяПользователяИБ, МенеджерНастроек)
	
	ТаблицаНастроек = Новый ТаблицаЗначений;
	ТаблицаНастроек.Колонки.Добавить("КлючОбъекта");
	ТаблицаНастроек.Колонки.Добавить("КлючНастроек");
	
	Отбор = Новый Структура;
	Отбор.Вставить("Пользователь", ИмяПользователяИБ);
	
	ВыборкаНастроек = МенеджерНастроек.Выбрать(Отбор);
	Пропустить = Ложь;
	Пока СледующаяНастройка(ВыборкаНастроек, Пропустить) Цикл
		
		Если Пропустить Тогда
			Продолжить;
		КонецЕсли;
		
		НоваяСтрока = ТаблицаНастроек.Добавить();
		НоваяСтрока.КлючОбъекта = ВыборкаНастроек.КлючОбъекта;
		НоваяСтрока.КлючНастроек = ВыборкаНастроек.КлючНастроек;
	КонецЦикла;
	
	Возврат ТаблицаНастроек;
	
КонецФункции

Функция СледующаяНастройка(ВыборкаНастроек, Пропустить) 
	
	Попытка 
		Пропустить = Ложь;
		Возврат ВыборкаНастроек.Следующий();
	Исключение
		Пропустить = Истина;
		Возврат Истина;
	КонецПопытки;
	
КонецФункции

Процедура СкопироватьНастройки(МенеджерНастроек, ИмяПользователяИсточник, ИмяПользователяПриемник, Переносить)
	
	ТаблицаНастроек = СписокНастроек(ИмяПользователяИсточник, МенеджерНастроек);
	
	Для Каждого Настройка Из ТаблицаНастроек Цикл
		КлючОбъекта = Настройка.КлючОбъекта;
		КлючНастроек = Настройка.КлючНастроек;
		Попытка
			Значение = МенеджерНастроек.Загрузить(КлючОбъекта, КлючНастроек, , ИмяПользователяИсточник);
		Исключение
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			Комментарий = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'При копировании настройки от пользователя ""%1""
				           |пользователю ""%2""
				           |с ключом объекта ""%3"" и
				           |ключом настроек ""%4""
				           |не удалось загрузить значение настройки по причине:
				           |%5'"),
				ИмяПользователяИсточник,
				ИмяПользователяПриемник,
				КлючОбъекта,
				КлючНастроек,
				ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
			ЗаписьЖурналаРегистрации(
				НСтр("ru = 'Пользователи.Копирование настроек'",
				     ОбщегоНазначения.КодОсновногоЯзыка()),
				УровеньЖурналаРегистрации.Ошибка,,,
				Комментарий);
			ВозможноПродолжить = Истина;
			Попытка
				Справочники.Пользователи.НайтиПоНаименованию(Строка(Новый УникальныйИдентификатор()), Истина);
			Исключение
				ВозможноПродолжить = Ложь;
			КонецПопытки;
			Если ВозможноПродолжить Тогда
				Продолжить;
			КонецЕсли;
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось скопировать настройку пользователю по причине:
				           |%1
				           |
				           |Подробности записаны в журнал регистрации.'"),
				ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
			ВызватьИсключение ТекстОшибки;
		КонецПопытки;
		ОписаниеНастроек = МенеджерНастроек.ПолучитьОписание(КлючОбъекта, КлючНастроек, ИмяПользователяИсточник);
		МенеджерНастроек.Сохранить(КлючОбъекта, КлючНастроек, Значение,
			ОписаниеНастроек, ИмяПользователяПриемник);
		Если Переносить Тогда
			МенеджерНастроек.Удалить(КлючОбъекта, КлючНастроек, ИмяПользователяИсточник);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

Процедура СкопироватьПрочиеПользовательскиеНастройки(ИмяПользователяИсточник, ИмяПользователяПриемник)
	
	ПользовательИсточникСсылка = Пользователи.НайтиПоИмени(ИмяПользователяИсточник);
	ПользовательПриемникСсылка = Пользователи.НайтиПоИмени(ИмяПользователяПриемник);
	СведенияОПользователеИсточнике = Новый Структура;
	СведенияОПользователеИсточнике.Вставить("ПользовательСсылка", ПользовательИсточникСсылка);
	СведенияОПользователеИсточнике.Вставить("ИмяПользователяИнформационнойБазы", ИмяПользователяИсточник);
	
	СведенияОПользователеПриемнике = Новый Структура;
	СведенияОПользователеПриемнике.Вставить("ПользовательСсылка", ПользовательПриемникСсылка);
	СведенияОПользователеПриемнике.Вставить("ИмяПользователяИнформационнойБазы", ИмяПользователяПриемник);
	
	// Получение прочих настроек.
	ПрочиеНастройкиПользователей = Новый Структура; // см. ПриПолученииПрочихНастроекПользователя.Настройки
	ПриПолученииПрочихНастроекПользователя(СведенияОПользователеИсточнике, ПрочиеНастройкиПользователей);
	Ключи = Новый СписокЗначений;
	
	Если ПрочиеНастройкиПользователей.Количество() <> 0 Тогда
		
		Для Каждого ПрочаяНастройка Из ПрочиеНастройкиПользователей Цикл
			ПрочиеНастройкиСтруктура = Новый Структура;
			Если ПрочаяНастройка.Ключ = "НастройкаБыстрогоДоступа" Тогда
				СписокНастроек = ПрочаяНастройка.Значение.СписокНастроек; // См. НовоеОписаниеНастройки
				Для Каждого Элемент Из СписокНастроек Цикл
					Ключи.Добавить(Элемент.Объект, Элемент.Идентификатор);
				КонецЦикла;
				ПрочиеНастройкиСтруктура.Вставить("ИдентификаторНастройки", "НастройкаБыстрогоДоступа");
				ПрочиеНастройкиСтруктура.Вставить("ЗначениеНастройки", Ключи);
			Иначе
				ПрочиеНастройкиСтруктура.Вставить("ИдентификаторНастройки", ПрочаяНастройка.Ключ);
				ПрочиеНастройкиСтруктура.Вставить("ЗначениеНастройки", ПрочаяНастройка.Значение.СписокНастроек);
			КонецЕсли;
			ПриСохраненииПрочихНастроекПользователя(СведенияОПользователеПриемнике, ПрочиеНастройкиСтруктура);
		КонецЦикла;
		
	КонецЕсли;
	
КонецПроцедуры

// Копирует настройки пользователя.
//
// Параметры:
//  ПользовательОбъект - СправочникОбъект.Пользователи
//                     - СправочникОбъект.ВнешниеПользователи
//  ПараметрыОбработки - Структура:
//    * ОписаниеНовогоПользователяИБ - см. Пользователи.НовоеОписаниеПользователяИБ
//
Процедура СкопироватьНастройкиПользователяИБ(ПользовательОбъект, ПараметрыОбработки)
	
	Если НЕ ПараметрыОбработки.Свойство("ЗначениеКопирования")
	 ИЛИ НЕ ПараметрыОбработки.НовыйПользовательИБСуществует Тогда
		
		Возврат;
	КонецЕсли;
	
	ИмяНовогоПользователяИБ = ПараметрыОбработки.ОписаниеНовогоПользователяИБ.Имя;
	
	ИдентификаторКопируемогоПользователяИБ = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(
		ПараметрыОбработки.ЗначениеКопирования, "ИдентификаторПользователяИБ");
	
	Если НЕ ЗначениеЗаполнено(ИдентификаторКопируемогоПользователяИБ) Тогда
		Возврат;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	ОписаниеКопируемогоПользователяИБ = Пользователи.СвойстваПользователяИБ(ИдентификаторКопируемогоПользователяИБ);
	Если ОписаниеКопируемогоПользователяИБ = Неопределено Тогда
		Возврат;
	КонецЕсли;
	УстановитьПривилегированныйРежим(Ложь);
	
	ИмяКопируемогоПользователяИБ = ОписаниеКопируемогоПользователяИБ.Имя;
	
	// Копирование настроек.
	СкопироватьНастройкиПользователя(ИмяКопируемогоПользователяИБ, ИмяНовогоПользователяИБ, Ложь);
	
КонецПроцедуры

Процедура ПроверитьСоставПравРолей(НедоступныеПрава, ОписаниеРолей, ОбщийТекстОшибки, ЗаголовокОшибки, СписокОшибок, НеразделенныеДанные = Неопределено)
	
	ТекстОшибки = "";
	
	Для Каждого ОписаниеРоли Из ОписаниеРолей Цикл
		Роль = ОписаниеРоли.Ключ;
		Для Каждого НедоступноеПраво Из НедоступныеПрава Цикл
			Если ПравоДоступа(НедоступноеПраво, Метаданные, Роль) Тогда
				ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Роль ""%1"" содержит недоступное право %2.'"),
					Роль, НедоступноеПраво);
				Если СписокОшибок = Неопределено Тогда
					ТекстОшибки = ТекстОшибки + Символы.ПС + ОписаниеОшибки;
				Иначе
					СписокОшибок.Добавить(Роль, ЗаголовокОшибки + Символы.ПС + ОписаниеОшибки);
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
		Если НеразделенныеДанные = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		Для Каждого СвойстваДанных Из НеразделенныеДанные Цикл
			ОбъектМетаданных = СвойстваДанных.Значение;
			Если Не ПравоДоступа("Чтение", ОбъектМетаданных, Роль) Тогда
				Продолжить;
			КонецЕсли;
			Если ПравоДоступа("Изменение", ОбъектМетаданных, Роль) Тогда
				ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Роль ""%1"" содержит право Изменение неразделенного объекта %2.'"),
					Роль, ОбъектМетаданных.ПолноеИмя());
				Если СписокОшибок = Неопределено Тогда
					ТекстОшибки = ТекстОшибки + Символы.ПС + ОписаниеОшибки;
				Иначе
					СписокОшибок.Добавить(ОбъектМетаданных, ЗаголовокОшибки + Символы.ПС + ОписаниеОшибки);
				КонецЕсли;
			КонецЕсли;
			Если СвойстваДанных.Представление = "" Тогда
				Продолжить; // Не ссылочный объект метаданных.
			КонецЕсли;
			Если ПравоДоступа("Добавление", ОбъектМетаданных, Роль) Тогда
				ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Роль ""%1"" содержит право Добавление неразделенного объекта %2.'"),
					Роль, ОбъектМетаданных.ПолноеИмя());
				Если СписокОшибок = Неопределено Тогда
					ТекстОшибки = ТекстОшибки + Символы.ПС + ОписаниеОшибки;
				Иначе
					СписокОшибок.Добавить(ОбъектМетаданных, ЗаголовокОшибки + Символы.ПС + ОписаниеОшибки);
				КонецЕсли;
			КонецЕсли;
			Если ПравоДоступа("Удаление", ОбъектМетаданных, Роль) Тогда
				ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Роль ""%1"" содержит право Удаление неразделенного объекта %2.'"),
					Роль, ОбъектМетаданных.ПолноеИмя());
				Если СписокОшибок = Неопределено Тогда
					ТекстОшибки = ТекстОшибки + Символы.ПС + ОписаниеОшибки;
				Иначе
					СписокОшибок.Добавить(ОбъектМетаданных, ЗаголовокОшибки + Символы.ПС + ОписаниеОшибки);
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	Если ЗначениеЗаполнено(ТекстОшибки) Тогда
		ОбщийТекстОшибки = ОбщийТекстОшибки + Символы.ПС + Символы.ПС
			+ ЗаголовокОшибки + ТекстОшибки;
	КонецЕсли;
	
КонецПроцедуры

Функция НеразделенныеДанные()
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("ТехнологияСервиса.БазоваяФункциональность") Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Список = Новый СписокЗначений;
	
	ВидыМетаданных = Новый Массив;
	ВидыМетаданных.Добавить(Новый Структура("Вид, Ссылочный" , Метаданные.ПланыОбмена,             Истина));
	ВидыМетаданных.Добавить(Новый Структура("Вид, Ссылочный" , Метаданные.Константы,               Ложь));
	ВидыМетаданных.Добавить(Новый Структура("Вид, Ссылочный" , Метаданные.Справочники,             Истина));
	ВидыМетаданных.Добавить(Новый Структура("Вид, Ссылочный" , Метаданные.Последовательности,      Ложь));
	ВидыМетаданных.Добавить(Новый Структура("Вид, Ссылочный" , Метаданные.Документы,               Истина));
	ВидыМетаданных.Добавить(Новый Структура("Вид, Ссылочный" , Метаданные.ПланыВидовХарактеристик, Истина));
	ВидыМетаданных.Добавить(Новый Структура("Вид, Ссылочный" , Метаданные.ПланыСчетов,             Истина));
	ВидыМетаданных.Добавить(Новый Структура("Вид, Ссылочный" , Метаданные.ПланыВидовРасчета,       Истина));
	ВидыМетаданных.Добавить(Новый Структура("Вид, Ссылочный" , Метаданные.БизнесПроцессы,          Истина));
	ВидыМетаданных.Добавить(Новый Структура("Вид, Ссылочный" , Метаданные.Задачи,                  Истина));
	ВидыМетаданных.Добавить(Новый Структура("Вид, Ссылочный" , Метаданные.РегистрыСведений,        Ложь));
	ВидыМетаданных.Добавить(Новый Структура("Вид, Ссылочный" , Метаданные.РегистрыНакопления,      Ложь));
	ВидыМетаданных.Добавить(Новый Структура("Вид, Ссылочный" , Метаданные.РегистрыБухгалтерии,     Ложь));
	ВидыМетаданных.Добавить(Новый Структура("Вид, Ссылочный" , Метаданные.РегистрыРасчета,         Ложь));
	
	УстановитьПривилегированныйРежим(Истина);
	
	МодульРаботаВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("РаботаВМоделиСервиса");
	МодельДанных = МодульРаботаВМоделиСервиса.ПолучитьМодельДанныхОбласти();
	
	РазделенныеОбъектыМетаданных = Новый Соответствие;
	Для Каждого ЭлементМоделиДанных Из МодельДанных Цикл
		ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ЭлементМоделиДанных.Ключ);
		РазделенныеОбъектыМетаданных.Вставить(ОбъектМетаданных, Истина);
	КонецЦикла;
	
	РазделениеВключено = ОбщегоНазначения.РазделениеВключено();
	ДоступноИспользованиеРазделенныхДанных = ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных();
	
	Для Каждого ОписаниеВида Из ВидыМетаданных Цикл // По видам метаданных.
		Для Каждого ОбъектМетаданных Из ОписаниеВида.Вид Цикл // По объектам вида.
			Если РазделенныеОбъектыМетаданных.Получить(ОбъектМетаданных) <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Если ДоступноИспользованиеРазделенныхДанных Тогда
				РасширениеКонфигурации = ОбъектМетаданных.РасширениеКонфигурации();
				Если РасширениеКонфигурации <> Неопределено
				   И (Не РазделениеВключено
				      Или РасширениеКонфигурации.ОбластьДействия = ОбластьДействияРасширенияКонфигурации.РазделениеДанных) Тогда
					Продолжить;
				КонецЕсли;
			КонецЕсли;
			Список.Добавить(ОбъектМетаданных, ?(ОписаниеВида.Ссылочный, "Ссылочный", ""));
		КонецЦикла;
	КонецЦикла;
	
	Возврат Список;
	
КонецФункции

Функция КлючПредупрежденияБезопасностиПриЗапуске()
	
	Если ПустаяСтрока(ПользователиИнформационнойБазы.ТекущийПользователь().Имя) Тогда
		Возврат Неопределено; // В базе без пользователей предупреждение не требуется.
	КонецЕсли;
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		Возврат Неопределено; // В модели сервиса предупреждение не требуется.
	КонецЕсли;
	
	Если ПривилегированныйРежим() Тогда
		Возврат Неопределено; // С ключом запуска "/UsePrivilegedMode" предупреждение не требуется.
	КонецЕсли;
	
	Если ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ()
		И Не ОбщегоНазначения.ЭтоАвтономноеРабочееМесто() Тогда
		Возврат Неопределено; // В подчиненных узлах предупреждение не требуется.
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	Если Не ПривилегированныйРежим() Тогда
		Возврат Неопределено; // В безопасном режиме предупреждение не требуется.
	КонецЕсли;
	
	ПараметрыАдминистрирования = СтандартныеПодсистемыСервер.ПараметрыАдминистрирования();
	РешениеПринято = ПараметрыАдминистрирования.ПринятоРешениеПоОткрытиюВнешнихОтчетовИОбработок;
	Если ТипЗнч(РешениеПринято) <> Тип("Булево") Тогда
		РешениеПринято = Ложь;
	КонецЕсли;
	УстановитьПривилегированныйРежим(Ложь);
	
	ЭтоАдминистраторСистемы = Пользователи.ЭтоПолноправныйПользователь(, Истина, Ложь);
	Если ЭтоАдминистраторСистемы И Не РешениеПринято Тогда
		Возврат "ПослеОбновления";
	КонецЕсли;
	
	Если РешениеПринято Тогда
		Если ПравоДоступа("ИнтерактивноеОткрытиеВнешнихОбработок", Метаданные)
		 Или ПравоДоступа("ИнтерактивноеОткрытиеВнешнихОтчетов", Метаданные) Тогда
			
			ПользовательОзнакомлен = ОбщегоНазначения.ХранилищеОбщихНастроекЗагрузить(
				"ПредупреждениеБезопасности", "ПользовательОзнакомлен", Ложь);
			
			Если Не ПользовательОзнакомлен Тогда
				Возврат "ПослеПоявленияПрава";
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Неопределено;
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//   * ТолькоДляАдминистраторовСистемы - Соответствие из КлючИЗначение:
//      ** Ключ     - ОбъектМетаданных - роль.
//      ** Значение - Булево - Истина.
//   * ТолькоДляПользователейСистемы - Соответствие из КлючИЗначение:
//      ** Ключ     - ОбъектМетаданных - роль.
//      ** Значение - Булево - Истина.
//   * ТолькоДляВнешнихПользователей - Соответствие из КлючИЗначение:
//      ** Ключ     - ОбъектМетаданных - роль.
//      ** Значение - Булево - Истина.
//   * СовместноДляПользователейИВнешнихПользователей - Соответствие из КлючИЗначение:
//      ** Ключ     - ОбъектМетаданных - роль.
//      ** Значение - Булево - Истина.
//
Функция НазначениеРолей()
	Возврат Новый Структура;
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Процедуры используемые при обмене данными.

// Переопределяет стандартное поведение при выгрузке данных.
// Реквизит ИдентификаторПользователяИБ не переносится.
//
Процедура ПриОтправкеДанных(ЭлементДанных, ОтправкаЭлемента, Подчиненному)
	
	Если ОтправкаЭлемента = ОтправкаЭлементаДанных.Удалить
	 ИЛИ ОтправкаЭлемента = ОтправкаЭлементаДанных.Игнорировать Тогда
		
		// Стандартная обработка не переопределяется.
		
	ИначеЕсли ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.Пользователи")
	      ИЛИ ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.ВнешниеПользователи") Тогда
		
		ЭлементДанных.ИдентификаторПользователяИБ = ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор();
		
		ЭлементДанных.Подготовлен = Ложь;
		ЭлементДанных.УдалитьСвойстваПользователяИБ = Новый ХранилищеЗначения(Неопределено);
	КонецЕсли;
	
КонецПроцедуры

// Переопределяет стандартное поведение при загрузке данных.
// Реквизит ИдентификаторПользователяИБ не переносится, т.к. всегда
// относится к пользователю текущей информационной базы или не заполнен.
//
Процедура ПриПолученииДанных(ЭлементДанных, ПолучениеЭлемента, ОтправкаНазад, ОтПодчиненного)
	
	Если ПолучениеЭлемента = ПолучениеЭлементаДанных.Игнорировать Тогда
		
		// Стандартная обработка не переопределяется.
		
	ИначеЕсли ТипЗнч(ЭлементДанных) = Тип("КонстантаМенеджерЗначения.ИспользоватьГруппыПользователей")
	      ИЛИ ТипЗнч(ЭлементДанных) = Тип("КонстантаМенеджерЗначения.ИспользоватьВнешнихПользователей")
	      ИЛИ ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.Пользователи")
	      ИЛИ ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.ГруппыПользователей")
	      ИЛИ ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.ВнешниеПользователи")
	      ИЛИ ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.ГруппыВнешнихПользователей")
	      ИЛИ ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.СоставыГруппПользователей") Тогда
		
		Если ОтПодчиненного И ОбщегоНазначения.РазделениеВключено() Тогда
			
			// Получение данных из автономного рабочего места пропускается, а для соответствия
			// данных в узлах, текущие данные отправляются обратно в автономное рабочее место.
			ОтправкаНазад = Истина;
			ПолучениеЭлемента = ПолучениеЭлементаДанных.Игнорировать;
			
		ИначеЕсли ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.Пользователи")
		      ИЛИ ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.ВнешниеПользователи") Тогда
			
			СписокСвойств =
				"ИдентификаторПользователяИБ,
				|Подготовлен,
				|УдалитьСвойстваПользователяИБ";
			
			ЗаполнитьЗначенияСвойств(ЭлементДанных, ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
				ЭлементДанных.Ссылка, СписокСвойств));
			
		ИначеЕсли ТипЗнч(ЭлементДанных) = Тип("УдалениеОбъекта") Тогда
			
			Если ТипЗнч(ЭлементДанных.Ссылка) = Тип("СправочникСсылка.Пользователи")
			 ИЛИ ТипЗнч(ЭлементДанных.Ссылка) = Тип("СправочникСсылка.ВнешниеПользователи") Тогда
				
				ОбъектПолучен = Ложь;
				Попытка
					Объект = ЭлементДанных.Ссылка.ПолучитьОбъект();
				Исключение
					ОбъектПолучен = Истина;
				КонецПопытки;
				
				Если ОбъектПолучен Тогда
					Объект.ОбщиеДействияПередУдалениемВОбычномРежимеИПриОбменеДанными();
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры обработки настроек входа.

// Для функции АвторизоватьТекущегоПользователяПриВходе.
// Обновляет дату последней активности и проверяет необходимость смены пароля.
//
Функция ТребуетсяСменитьПароль(ОписаниеОшибки = "", ПриЗапуске = Ложь, РегистрироватьВЖурнале = Истина)
	
	ПользовательИБ = ПользователиИнформационнойБазы.ТекущийПользователь();
	Если Не ЗначениеЗаполнено(ПользовательИБ.Имя) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	// Обновление даты последнего входа пользователя.
	УстановитьПривилегированныйРежим(Истина);
	ТекущийПользователь = Пользователи.АвторизованныйПользователь();
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ТекущийПользователь", ТекущийПользователь);
	
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	СведенияОПользователях.Пользователь КАК Пользователь,
	|	СведенияОПользователях.ДатаПоследнейАктивности КАК ДатаПоследнейАктивности,
	|	СведенияОПользователях.ПоследнийИспользуемыйКлиент КАК ПоследнийИспользуемыйКлиент,
	|	СведенияОПользователях.УдалитьДатаНачалаИспользованияПароля КАК УдалитьДатаНачалаИспользованияПароля,
	|	СведенияОПользователях.ДатаАвтоматическогоЗапрещенияВхода КАК ДатаАвтоматическогоЗапрещенияВхода,
	|	СведенияОПользователях.ПотребоватьСменуПароляПриВходе КАК ПотребоватьСменуПароляПриВходе
	|ИЗ
	|	РегистрСведений.СведенияОПользователях КАК СведенияОПользователях
	|ГДЕ
	|	СведенияОПользователях.Пользователь = &ТекущийПользователь";
	
	Выгрузка = Запрос.Выполнить().Выгрузить();
	
	НачалоДняТекущейДатыСеанса = НачалоДня(ТекущаяДатаСеанса());
	ИспользуемыйКлиент = ОбщегоНазначения.ИспользуемыйКлиент();
	
	Если Выгрузка.Количество() = 1 Тогда
		ЕстьИзменения = Ложь;
		УстарелаТолькоДатаПоследнейАктивности = Истина;
		СведенияОПользователе = Выгрузка[0];
		ОбновитьСведенияОПользователе(СведенияОПользователе, НачалоДняТекущейДатыСеанса,
			ИспользуемыйКлиент, ЕстьИзменения, УстарелаТолькоДатаПоследнейАктивности, Истина);
	Иначе
		ЕстьИзменения = Истина;
		УстарелаТолькоДатаПоследнейАктивности = Ложь;
	КонецЕсли;
	
	Если ЕстьИзменения Тогда
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.СведенияОПользователях");
		ЭлементБлокировки.УстановитьЗначение("Пользователь", ТекущийПользователь);
		НачатьТранзакцию();
		Попытка
			БлокировкаВыполняется = Истина;
			Блокировка.Заблокировать();
			БлокировкаВыполняется = Ложь;
			НаборЗаписей = РегистрыСведений.СведенияОПользователях.СоздатьНаборЗаписей();
			НаборЗаписей.Отбор.Пользователь.Установить(ТекущийПользователь);
			НаборЗаписей.Прочитать();
			Если НаборЗаписей.Количество() = 0 Тогда
				СведенияОПользователе = НаборЗаписей.Добавить();
				СведенияОПользователе.Пользователь = ТекущийПользователь;
			Иначе
				СведенияОПользователе = НаборЗаписей[0];
			КонецЕсли;
			
			Записать = Ложь;
			ОбновитьСведенияОПользователе(СведенияОПользователе,
				НачалоДняТекущейДатыСеанса, ИспользуемыйКлиент, Записать);
			
			Если Записать Тогда
				НаборЗаписей.Записать();
			КонецЕсли;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ПродолжитьЗапуск = БлокировкаВыполняется И УстарелаТолькоДатаПоследнейАктивности;
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			ШаблонТекстаОшибки = ШаблонТекстаОшибкиЗаписиСведенийОТекущемПользователе();
			Если ПриЗапуске И Не ПродолжитьЗапуск Тогда
				ОписаниеОшибки = ЗаголовокСообщенияАвторизацияНеВыполненаСПереводомСтроки()
					+ СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонТекстаОшибки,
						ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
				
				Если РегистрироватьВЖурнале Тогда
					ЗаписьЖурналаРегистрации(
						НСтр("ru = 'Пользователи.Ошибка входа в программу'",
						     ОбщегоНазначения.КодОсновногоЯзыка()),
						УровеньЖурналаРегистрации.Ошибка,
						Метаданные.НайтиПоТипу(ТипЗнч(ТекущийПользователь)),
						ТекущийПользователь,
						СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонТекстаОшибки,
							ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)));
				КонецЕсли;
			Иначе
				Если РегистрироватьВЖурнале Тогда
					ЗаписьЖурналаРегистрации(
						НСтр("ru = 'Пользователи.Ошибка обновления даты последней активности'",
						     ОбщегоНазначения.КодОсновногоЯзыка()),
						УровеньЖурналаРегистрации.Ошибка,
						Метаданные.НайтиПоТипу(ТипЗнч(ТекущийПользователь)),
						ТекущийПользователь,
						СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонТекстаОшибки,
							ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)));
				КонецЕсли;
			КонецЕсли;
			Если Не ПриЗапуске Или Не ПродолжитьЗапуск Тогда
				Возврат Ложь;
			КонецЕсли;
		КонецПопытки;
	КонецЕсли;
	УстановитьПривилегированныйРежим(Ложь);
	
	Если СтандартныеПодсистемыСервер.ЭтоРазделенныйРежимСеансаБезРазделителей() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ДополнительныеПараметры = Новый Структура;
	ДополнительныеПараметры.Вставить("УчитыватьСвойствоЗапрещеноИзменятьПароль");
	ДополнительныеПараметры.Вставить("УчитыватьСвойствоАутентификацияСтандартная");
	Если Не ВозможноИзменитьПароль(ТекущийПользователь, ДополнительныеПараметры) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если СведенияОПользователе.ПотребоватьСменуПароляПриВходе Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если Не ОбщегоНазначения.РазделениеВключено()
	 Или Не Пользователи.ОбщиеНастройкиВходаИспользуются() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ТипЗнч(ТекущийПользователь) = Тип("СправочникСсылка.ВнешниеПользователи") Тогда
		НастройкиВхода = НастройкиВхода().ВнешниеПользователи;
	Иначе
		НастройкиВхода = НастройкиВхода().Пользователи;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(НастройкиВхода.МаксимальныйСрокДействияПароля) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(СведенияОПользователе.УдалитьДатаНачалаИспользованияПароля) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ОстатокМаксимальногоСрокаДействияПароля = НастройкиВхода.МаксимальныйСрокДействияПароля
		- (НачалоДняТекущейДатыСеанса - СведенияОПользователе.УдалитьДатаНачалаИспользованияПароля) / (24*60*60);
	
	Возврат ОстатокМаксимальногоСрокаДействияПароля <= 0;
	
КонецФункции

// Для функции ТребуетсяСменитьПароль.
Процедура ОбновитьСведенияОПользователе(СведенияОПользователе, НачалоДняТекущейДатыСеанса,
			ИспользуемыйКлиент, ЕстьИзменения, УстарелаТолькоДатаПоследнейАктивности = Истина, ЭтоПроверка = Ложь)
	
	Если СведенияОПользователе.ДатаПоследнейАктивности <> НачалоДняТекущейДатыСеанса Тогда
		
		СведенияОПользователе.ДатаПоследнейАктивности = НачалоДняТекущейДатыСеанса;
		ЕстьИзменения = Истина;
		
		Если Не ЭтоПроверка
		   И ОбщегоНазначения.РазделениеВключено()
		   И ОбщегоНазначения.ПодсистемаСуществует("ТехнологияСервиса.ПользователиСервиса") Тогда
			
			МодульПользователиСервиса = ОбщегоНазначения.ОбщийМодуль("ПользователиСервиса");
			МодульПользователиСервиса.ОбновитьАктивностьПользователяСервиса(
				СведенияОПользователе.Пользователь,
				НачалоДняТекущейДатыСеанса);
		КонецЕсли;
	КонецЕсли;
	
	Если ИспользуемыйКлиент <> Неопределено
	   И СведенияОПользователе.ПоследнийИспользуемыйКлиент <> ИспользуемыйКлиент Тогда
		
		СведенияОПользователе.ПоследнийИспользуемыйКлиент = ИспользуемыйКлиент;
		ЕстьИзменения = Истина;
		УстарелаТолькоДатаПоследнейАктивности = Ложь;
	КонецЕсли;
	
	Если Не ОбщегоНазначения.РазделениеВключено() Тогда
		Если ЗначениеЗаполнено(СведенияОПользователе.УдалитьДатаНачалаИспользованияПароля) Тогда
			СведенияОПользователе.УдалитьДатаНачалаИспользованияПароля = Неопределено;
			ЕстьИзменения = Истина;
		КонецЕсли;
		
	ИначеЕсли Не ЗначениеЗаполнено(СведенияОПользователе.УдалитьДатаНачалаИспользованияПароля)
	      Или СведенияОПользователе.УдалитьДатаНачалаИспользованияПароля > НачалоДняТекущейДатыСеанса Тогда
		
		СведенияОПользователе.УдалитьДатаНачалаИспользованияПароля = НачалоДняТекущейДатыСеанса;
		ЕстьИзменения = Истина;
		УстарелаТолькоДатаПоследнейАктивности = Ложь;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(СведенияОПользователе.ДатаАвтоматическогоЗапрещенияВхода) Тогда
		СведенияОПользователе.ДатаАвтоматическогоЗапрещенияВхода = Неопределено;
		ЕстьИзменения = Истина;
		УстарелаТолькоДатаПоследнейАктивности = Ложь;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ПриОтправкеСерверногоОповещения.
Процедура ОбновитьДатуПоследнейАктивностиПользователей(Параметры)
	
	ИдентификаторыПользователейИБ = Новый Массив;
	Для Каждого ПараметрПроверки Из Параметры Цикл
		Для Каждого Адресат Из ПараметрПроверки.Адресаты Цикл
			ИдентификаторыПользователейИБ.Добавить(Адресат.Ключ);
		КонецЦикла;
	КонецЦикла;
	
	НачалоДняТекущейДатыСеанса = НачалоДня(ТекущаяДатаСеанса());
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("НачалоДняТекущейДатыСеанса",    НачалоДняТекущейДатыСеанса);
	Запрос.УстановитьПараметр("ИдентификаторыПользователейИБ", ИдентификаторыПользователейИБ);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	СведенияОПользователях.Пользователь КАК Пользователь
	|ИЗ
	|	РегистрСведений.СведенияОПользователях КАК СведенияОПользователях
	|ГДЕ
	|	СведенияОПользователях.ДатаПоследнейАктивности < &НачалоДняТекущейДатыСеанса
	|	И СведенияОПользователях.Пользователь.ИдентификаторПользователяИБ В(&ИдентификаторыПользователейИБ)";
	
	Выборка = Запрос.Выполнить().Выбрать();
	
	Пока Выборка.Следующий() Цикл
		ТекущийПользователь = Выборка.Пользователь;
		
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.СведенияОПользователях");
		ЭлементБлокировки.УстановитьЗначение("Пользователь", ТекущийПользователь);
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			НаборЗаписей = РегистрыСведений.СведенияОПользователях.СоздатьНаборЗаписей();
			НаборЗаписей.Отбор.Пользователь.Установить(ТекущийПользователь);
			НаборЗаписей.Прочитать();
			Если НаборЗаписей.Количество() = 1 Тогда
				СведенияОПользователе = НаборЗаписей[0];
				Если СведенияОПользователе.ДатаПоследнейАктивности < НачалоДняТекущейДатыСеанса Тогда
					СведенияОПользователе.ДатаПоследнейАктивности = НачалоДняТекущейДатыСеанса;
					
					Если ОбщегоНазначения.РазделениеВключено()
						И ОбщегоНазначения.ПодсистемаСуществует("ТехнологияСервиса.ПользователиСервиса") Тогда
						
						МодульПользователиСервиса = ОбщегоНазначения.ОбщийМодуль("ПользователиСервиса");
						МодульПользователиСервиса.ОбновитьАктивностьПользователяСервиса(
							ТекущийПользователь,
							НачалоДняТекущейДатыСеанса);
					КонецЕсли;
					
					НаборЗаписей.Записать();
				КонецЕсли;
			КонецЕсли;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			ШаблонТекстаОшибки = ШаблонТекстаОшибкиЗаписиСведенийОТекущемПользователе();
			ЗаписьЖурналаРегистрации(
				НСтр("ru = 'Пользователи.Ошибка обновления даты последней активности'",
				     ОбщегоНазначения.КодОсновногоЯзыка()),
				УровеньЖурналаРегистрации.Ошибка,
				Метаданные.НайтиПоТипу(ТипЗнч(ТекущийПользователь)),
				ТекущийПользователь,
				СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонТекстаОшибки,
					ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)));
		КонецПопытки;
	КонецЦикла;
	
КонецПроцедуры

// Для функции ОбработатьНовыйПароль и
// процедуры Пользователи.УстановитьСвойстваПользователяИБ.
//
// Параметры:
//  Пароль - Строка
//  ПользовательИБ - ПользовательИнформационнойБазы
//
// Возвращаемое значение:
//  Строка
//
Функция ОшибкаСоответствияПароляТребованиям(Пароль, ПользовательИБ) Экспорт
	
	ПолитикаПаролей = ПолитикиПаролейПользователей.НайтиПоИмени(ПользовательИБ.ИмяПолитикиПаролей);
	
	Ошибки = ПолитикиПаролейПользователей.ПроверитьСоответствиеПароляПолитике(Пароль,
		ПолитикаПаролей, ПользовательИБ);
	
	Если Ошибки.Найти(РезультатПроверкиСоответствияПароляПолитике.НеСоответствуетТребованиямМинимальнойДлины) <> Неопределено Тогда
		Если ПолитикаПаролей = Неопределено Тогда
			МинимальнаяДлинаПароля = ПолучитьМинимальнуюДлинуПаролейПользователей();
		Иначе
			МинимальнаяДлинаПароля = ПолитикаПаролей.МинимальнаяДлинаПаролей;
		КонецЕсли;
		Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Количество символов нового пароля должно быть не менее %1.'"),
			Формат(МинимальнаяДлинаПароля, "ЧГ="));
	КонецЕсли;
	
	Если Ошибки.Найти(РезультатПроверкиСоответствияПароляПолитике.НеСоответствуетТребованиямСложности) <> Неопределено Тогда
		Возврат НСтр("ru = 'Пароль не отвечает требованиям сложности.'")
			+ Символы.ПС + Символы.ПС
			+ ПодсказкаДляНовогоПароля();
	КонецЕсли;
	
	Если Ошибки.Найти(РезультатПроверкиСоответствияПароляПолитике.НеСоответствуетТребованиямОграниченияПовторенияСредиПоследних) <> Неопределено Тогда
		Возврат НСтр("ru = 'Новый пароль использовался ранее.'");
	КонецЕсли;
	
	Возврат "";
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Для процедуры НачатьОбработкуПользователяИБ.

Процедура ЗапомнитьСвойстваПользователя(ПользовательОбъект, ПараметрыОбработки)
	
	Поля =
	"Ссылка,
	|ИдентификаторПользователяИБ,
	|ИдентификаторПользователяСервиса,
	|УдалитьСвойстваПользователяИБ,
	|Подготовлен,
	|ПометкаУдаления,
	|Недействителен";
	
	Если ТипЗнч(ПользовательОбъект) = Тип("СправочникОбъект.Пользователи") Тогда
		Поля = Поля + ",
		|Служебный";
	КонецЕсли;
	
	СтарыйПользователь = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ПользовательОбъект.Ссылка, Поля);
	
	Если ТипЗнч(ПользовательОбъект) <> Тип("СправочникОбъект.Пользователи") Тогда
		СтарыйПользователь.Вставить("Служебный", Ложь);
	КонецЕсли;
	
	Если ПользовательОбъект.ЭтоНовый() Или ПользовательОбъект.Ссылка <> СтарыйПользователь.Ссылка Тогда
		СтарыйПользователь.ИдентификаторПользователяИБ = ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор();
		СтарыйПользователь.ИдентификаторПользователяСервиса = ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор();
		СтарыйПользователь.УдалитьСвойстваПользователяИБ    = Новый ХранилищеЗначения(Неопределено);
		СтарыйПользователь.Подготовлен               = Ложь;
		СтарыйПользователь.ПометкаУдаления           = Ложь;
		СтарыйПользователь.Недействителен            = Ложь;
	КонецЕсли;
	ПараметрыОбработки.Вставить("СтарыйПользователь", СтарыйПользователь);
	
	// Свойства старого пользователя ИБ (если существует).
	УстановитьПривилегированныйРежим(Истина);
	
	ОписаниеСтарогоПользователяИБ = Пользователи.СвойстваПользователяИБ(СтарыйПользователь.ИдентификаторПользователяИБ);
	ПараметрыОбработки.Вставить("СтарыйПользовательИБСуществует", ОписаниеСтарогоПользователяИБ <> Неопределено);
	ПараметрыОбработки.Вставить("СтарыйПользовательИБТекущий", Ложь);
	ПараметрыОбработки.Вставить("СтарыйПользовательИБ",
		ПользовательИБПоИдентификатору(СтарыйПользователь.ИдентификаторПользователяИБ));
	
	Если ПараметрыОбработки.СтарыйПользовательИБСуществует Тогда
		ПараметрыОбработки.Вставить("ОписаниеСтарогоПользователяИБ", ОписаниеСтарогоПользователяИБ);
		
		Если ОписаниеСтарогоПользователяИБ.УникальныйИдентификатор =
				ПользователиИнформационнойБазы.ТекущийПользователь().УникальныйИдентификатор Тогда
		
			ПараметрыОбработки.Вставить("СтарыйПользовательИБТекущий", Истина);
		КонецЕсли;
	КонецЕсли;
	УстановитьПривилегированныйРежим(Ложь);
	
	// Начальное заполнение значений полей авто реквизитов значениями старого пользователя.
	ЗаполнитьЗначенияСвойств(ПараметрыОбработки.АвтоРеквизиты, СтарыйПользователь);
	
	// Начальное заполнение значений полей блокируемых реквизитов значениями нового пользователя.
	ЗаполнитьЗначенияСвойств(ПараметрыОбработки.БлокируемыеРеквизиты, ПользовательОбъект);
	
КонецПроцедуры

Процедура ЗаписатьПользователяИБ(ПользовательОбъект, ПараметрыОбработки)
	
	ДополнительныеСвойства = ПользовательОбъект.ДополнительныеСвойства;
	ОписаниеПользователяИБ = ДополнительныеСвойства.ОписаниеПользователяИБ;
	СтарыйПользователь     = ПараметрыОбработки.СтарыйПользователь;
	
	Если ОписаниеПользователяИБ.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Если ОписаниеПользователяИБ.Свойство("ПотребоватьСменуПароляПриВходе") Тогда
		ЗаписатьСвойствоПотребоватьСменуПароляПриВходе(СсылкаОбъекта(ПользовательОбъект),
			ОписаниеПользователяИБ.ПотребоватьСменуПароляПриВходе);
		
		Если ОписаниеПользователяИБ.Количество() = 2 Тогда
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	СоздатьНовогоПользователяИБ = Ложь;
	
	Если ОписаниеПользователяИБ.Свойство("УникальныйИдентификатор")
	   И ЗначениеЗаполнено(ОписаниеПользователяИБ.УникальныйИдентификатор)
	   И ОписаниеПользователяИБ.УникальныйИдентификатор
	     <> ПараметрыОбработки.СтарыйПользователь.ИдентификаторПользователяИБ Тогда
		
		ИдентификаторПользователяИБ = ОписаниеПользователяИБ.УникальныйИдентификатор;
		
	ИначеЕсли ЗначениеЗаполнено(СтарыйПользователь.ИдентификаторПользователяИБ) Тогда
		ИдентификаторПользователяИБ = СтарыйПользователь.ИдентификаторПользователяИБ;
		СоздатьНовогоПользователяИБ = НЕ ПараметрыОбработки.СтарыйПользовательИБСуществует;
	Иначе
		ИдентификаторПользователяИБ = ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор();
		СоздатьНовогоПользователяИБ = Истина;
	КонецЕсли;
	
	// Заполнение автоматических свойств пользователя ИБ.
	ОписаниеПользователяИБ.Вставить("ПолноеИмя", ПользовательОбъект.Наименование);
	
	ХранимыеСвойства = ХранимыеСвойстваПользователяИБ(СсылкаОбъекта(ПользовательОбъект));
	Если ПараметрыОбработки.СтарыйПользовательИБСуществует Тогда
		СтараяАутентификация = ПараметрыОбработки.ОписаниеСтарогоПользователяИБ;
		Если Пользователи.ВходВПрограммуРазрешен(СтараяАутентификация) Тогда
			ХранимыеСвойства.АутентификацияСтандартная    = СтараяАутентификация.АутентификацияСтандартная;
			ХранимыеСвойства.АутентификацияOpenID         = СтараяАутентификация.АутентификацияOpenID;
			ХранимыеСвойства.АутентификацияOpenIDConnect  = СтараяАутентификация.АутентификацияOpenIDConnect;
			ХранимыеСвойства.АутентификацияТокеномДоступа = СтараяАутентификация.АутентификацияТокеномДоступа;
			ХранимыеСвойства.АутентификацияОС             = СтараяАутентификация.АутентификацияОС;
			УстановитьХранимыеСвойстваПользователяИБ(ПользовательОбъект, ХранимыеСвойства);
		КонецЕсли;
	Иначе
		СтараяАутентификация = Новый Структура;
		СтараяАутентификация.Вставить("АутентификацияСтандартная",    Ложь);
		СтараяАутентификация.Вставить("АутентификацияOpenID",         Ложь);
		СтараяАутентификация.Вставить("АутентификацияOpenIDConnect",  Ложь);
		СтараяАутентификация.Вставить("АутентификацияТокеномДоступа", Ложь);
		СтараяАутентификация.Вставить("АутентификацияОС",             Ложь);
		ХранимыеСвойства.АутентификацияСтандартная    = Ложь;
		ХранимыеСвойства.АутентификацияOpenID         = Ложь;
		ХранимыеСвойства.АутентификацияOpenIDConnect  = Ложь;
		ХранимыеСвойства.АутентификацияТокеномДоступа = Ложь;
		ХранимыеСвойства.АутентификацияОС             = Ложь;
		УстановитьХранимыеСвойстваПользователяИБ(ПользовательОбъект, ХранимыеСвойства);
	КонецЕсли;
	
	Если ОписаниеПользователяИБ.Свойство("АутентификацияСтандартная") Тогда
		ХранимыеСвойства.АутентификацияСтандартная = ОписаниеПользователяИБ.АутентификацияСтандартная;
		УстановитьХранимыеСвойстваПользователяИБ(ПользовательОбъект, ХранимыеСвойства);
	КонецЕсли;
	
	Если ОписаниеПользователяИБ.Свойство("АутентификацияOpenID") Тогда
		ХранимыеСвойства.АутентификацияOpenID = ОписаниеПользователяИБ.АутентификацияOpenID;
		УстановитьХранимыеСвойстваПользователяИБ(ПользовательОбъект, ХранимыеСвойства);
	КонецЕсли;
	
	Если ОписаниеПользователяИБ.Свойство("АутентификацияOpenIDConnect") Тогда
		ХранимыеСвойства.АутентификацияOpenIDConnect = ОписаниеПользователяИБ.АутентификацияOpenIDConnect;
		УстановитьХранимыеСвойстваПользователяИБ(ПользовательОбъект, ХранимыеСвойства);
	КонецЕсли;
	
	Если ОписаниеПользователяИБ.Свойство("АутентификацияТокеномДоступа") Тогда
		ХранимыеСвойства.АутентификацияТокеномДоступа = ОписаниеПользователяИБ.АутентификацияТокеномДоступа;
		УстановитьХранимыеСвойстваПользователяИБ(ПользовательОбъект, ХранимыеСвойства);
	КонецЕсли;
	
	Если ОписаниеПользователяИБ.Свойство("АутентификацияОС") Тогда
		ХранимыеСвойства.АутентификацияОС = ОписаниеПользователяИБ.АутентификацияОС;
		УстановитьХранимыеСвойстваПользователяИБ(ПользовательОбъект, ХранимыеСвойства);
	КонецЕсли;
	
	УстановитьХранимуюАутентификацию = Неопределено;
	Если ОписаниеПользователяИБ.Свойство("ВходВПрограммуРазрешен") Тогда
		УстановитьХранимуюАутентификацию = ОписаниеПользователяИБ.ВходВПрограммуРазрешен = Истина;
	
	ИначеЕсли ОписаниеПользователяИБ.Свойство("АутентификацияСтандартная")
	        И ОписаниеПользователяИБ.АутентификацияСтандартная = Истина
	      Или ОписаниеПользователяИБ.Свойство("АутентификацияOpenID")
	        И ОписаниеПользователяИБ.АутентификацияOpenID = Истина
	      Или ОписаниеПользователяИБ.Свойство("АутентификацияOpenIDConnect")
	        И ОписаниеПользователяИБ.АутентификацияOpenIDConnect = Истина
	      Или ОписаниеПользователяИБ.Свойство("АутентификацияТокеномДоступа")
	        И ОписаниеПользователяИБ.АутентификацияТокеномДоступа = Истина
	      Или ОписаниеПользователяИБ.Свойство("АутентификацияОС")
	        И ОписаниеПользователяИБ.АутентификацияОС = Истина Тогда
		
		УстановитьХранимуюАутентификацию = Истина;
	КонецЕсли;
	
	Если УстановитьХранимуюАутентификацию = Неопределено Тогда
		НоваяАутентификация = СтараяАутентификация;
	Иначе
		Если УстановитьХранимуюАутентификацию Тогда
			ОписаниеПользователяИБ.Вставить("АутентификацияСтандартная",    ХранимыеСвойства.АутентификацияСтандартная);
			ОписаниеПользователяИБ.Вставить("АутентификацияOpenID",         ХранимыеСвойства.АутентификацияOpenID);
			ОписаниеПользователяИБ.Вставить("АутентификацияOpenIDConnect",  ХранимыеСвойства.АутентификацияOpenIDConnect);
			ОписаниеПользователяИБ.Вставить("АутентификацияТокеномДоступа", ХранимыеСвойства.АутентификацияТокеномДоступа);
			ОписаниеПользователяИБ.Вставить("АутентификацияОС",             ХранимыеСвойства.АутентификацияОС);
		Иначе
			ОписаниеПользователяИБ.Вставить("АутентификацияСтандартная",    Ложь);
			ОписаниеПользователяИБ.Вставить("АутентификацияOpenID",         Ложь);
			ОписаниеПользователяИБ.Вставить("АутентификацияOpenIDConnect",  Ложь);
			ОписаниеПользователяИБ.Вставить("АутентификацияТокеномДоступа", Ложь);
			ОписаниеПользователяИБ.Вставить("АутентификацияОС",             Ложь);
		КонецЕсли;
		НоваяАутентификация = ОписаниеПользователяИБ;
	КонецЕсли;
	
	Если ХранимыеСвойства.ВходВПрограммуРазрешен <> Пользователи.ВходВПрограммуРазрешен(НоваяАутентификация) Тогда
		ХранимыеСвойства.ВходВПрограммуРазрешен = Пользователи.ВходВПрограммуРазрешен(НоваяАутентификация);
		УстановитьХранимыеСвойстваПользователяИБ(ПользовательОбъект, ХранимыеСвойства);
	КонецЕсли;
	
	// Проверка прав на изменение разрешения входа в программу.
	Если Пользователи.ВходВПрограммуРазрешен(НоваяАутентификация)
	  <> Пользователи.ВходВПрограммуРазрешен(СтараяАутентификация) Тогда
	
		Если Пользователи.ВходВПрограммуРазрешен(НоваяАутентификация)
		   И Не ПараметрыОбработки.УровеньДоступа.ИзменениеРазрешенияНаВход
		 Или Не Пользователи.ВходВПрограммуРазрешен(НоваяАутентификация)
		   И Не ПараметрыОбработки.УровеньДоступа.ОтключениеРазрешенияНаВход Тогда
			
			ВызватьИсключение ПараметрыОбработки.ТекстСообщенияНедостаточноПрав;
		КонецЕсли;
	КонецЕсли;
	
	ПарольЗадан = ОписаниеПользователяИБ.Свойство("Пароль")
		И ОписаниеПользователяИБ.Пароль <> Неопределено;
	
	ХешПароляЗадан = ОписаниеПользователяИБ.Свойство("СохраняемоеЗначениеПароля")
		И ОписаниеПользователяИБ.СохраняемоеЗначениеПароля <> Неопределено;
	
	ПроверитьПустойПароль =
		Не СтараяАутентификация.АутентификацияСтандартная
		И ХранимыеСвойства.АутентификацияСтандартная
		И (    СоздатьНовогоПользователяИБ
		     И Не ПарольЗадан
		   Или ПараметрыОбработки.СтарыйПользовательИБСуществует
		     И Не ПараметрыОбработки.ОписаниеСтарогоПользователяИБ.ПарольУстановлен);
	
	Если ПарольЗадан Или Не ХешПароляЗадан И ПроверитьПустойПароль Тогда
		ИмяДляВхода = ?(ОписаниеПользователяИБ.Свойство("Имя"),
			ОписаниеПользователяИБ.Имя, ?(ПараметрыОбработки.СтарыйПользовательИБСуществует,
				ПараметрыОбработки.ОписаниеСтарогоПользователяИБ.Имя, ""));
		ПараметрыВыполнения = Новый Структура;
		ПараметрыВыполнения.Вставить("Пользователь", ПользовательОбъект);
		ПараметрыВыполнения.Вставить("ИмяДляВхода",  ИмяДляВхода);
		ПараметрыВыполнения.Вставить("НовыйПароль", ?(ПарольЗадан, ОписаниеПользователяИБ.Пароль, ""));
		ПараметрыВыполнения.Вставить("СтарыйПароль", Неопределено);
		
		ОписаниеПользователяИБ.Свойство("СтарыйПароль", ПараметрыВыполнения.СтарыйПароль);
		
		ТекстОшибки = ОбработатьНовыйПароль(ПараметрыВыполнения);
		Если ЗначениеЗаполнено(ТекстОшибки) Тогда
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
	КонецЕсли;
	
	// Попытка записи пользователя ИБ.
	ПараметрыОбновления = Новый Соответствие(ПараметрыСеанса.ОбновлениеСправочниковПользователей);
	ПараметрыОбновления.Вставить("ИдентификаторПользователяИБ", ИдентификаторПользователяИБ);
	ПараметрыСеанса.ОбновлениеСправочниковПользователей = Новый ФиксированноеСоответствие(ПараметрыОбновления);
	ПараметрыОбновления.Удалить("ИдентификаторПользователяИБ");
	Попытка
		Пользователи.УстановитьСвойстваПользователяИБ(ИдентификаторПользователяИБ, ОписаниеПользователяИБ, 
			СоздатьНовогоПользователяИБ, ТипЗнч(ПользовательОбъект) = Тип("СправочникОбъект.ВнешниеПользователи"));
		ПользовательИБ = ОписаниеПользователяИБ.ПользовательИБ;
	Исключение
		ПараметрыСеанса.ОбновлениеСправочниковПользователей = Новый ФиксированноеСоответствие(ПараметрыОбновления);
		ВызватьИсключение;
	КонецПопытки;
	ПараметрыСеанса.ОбновлениеСправочниковПользователей = Новый ФиксированноеСоответствие(ПараметрыОбновления);
	
	Если ПользовательОбъект.ДополнительныеСвойства.Свойство("СозданиеАдминистратора")
	   И ЗначениеЗаполнено(ПользовательОбъект.ДополнительныеСвойства.СозданиеАдминистратора)
	   И РолиАдминистратораДоступны(ПользовательИБ) Тогда
		
		ПараметрыОбработки.Вставить("СозданиеАдминистратора",
			ПользовательОбъект.ДополнительныеСвойства.СозданиеАдминистратора);
	КонецЕсли;
	
	Если СоздатьНовогоПользователяИБ Тогда
		ОписаниеПользователяИБ.Вставить("РезультатДействия", "ДобавленПользовательИБ");
		ИдентификаторПользователяИБ = ОписаниеПользователяИБ.УникальныйИдентификатор;
		ПараметрыОбработки.Вставить("УстановкаПользователяИБ");
		
		Если Не ПараметрыОбработки.УровеньДоступа.ИзменениеРазрешенияНаВход
		   И ПараметрыОбработки.УровеньДоступа.УправлениеСписком
		   И Не Пользователи.ВходВПрограммуРазрешен(ПользовательИБ) Тогда
			
			ПользовательОбъект.Подготовлен = Истина;
			ПараметрыОбработки.БлокируемыеРеквизиты.Подготовлен = Истина;
		КонецЕсли;
	Иначе
		ОписаниеПользователяИБ.Вставить("РезультатДействия", "ИзмененПользовательИБ");
		
		Если Пользователи.ВходВПрограммуРазрешен(ПользовательИБ) Тогда
			ПользовательОбъект.Подготовлен = Ложь;
			ПараметрыОбработки.БлокируемыеРеквизиты.Подготовлен = Ложь;
		КонецЕсли;
	КонецЕсли;
	
	ПользовательОбъект.ИдентификаторПользователяИБ = ИдентификаторПользователяИБ;
	
	ОписаниеПользователяИБ.Вставить("УникальныйИдентификатор", ИдентификаторПользователяИБ);
	
КонецПроцедуры

Процедура УдалитьПользователяИБ(ПользовательОбъект, ПараметрыОбработки)
	
	ОписаниеПользователяИБ = ПользовательОбъект.ДополнительныеСвойства.ОписаниеПользователяИБ;
	СтарыйПользователь     = ПараметрыОбработки.СтарыйПользователь;
	
	// Очистка идентификатора пользователя ИБ.
	ПользовательОбъект.ИдентификаторПользователяИБ = Неопределено;
	
	Если ПараметрыОбработки.СтарыйПользовательИБСуществует Тогда
		
		УстановитьПривилегированныйРежим(Истина);
		Пользователи.УдалитьПользователяИБ(СтарыйПользователь.ИдентификаторПользователяИБ);
			
		// Установка идентификатора удаляемого пользователя ИБ, как результата действия Удалить.
		ОписаниеПользователяИБ.Вставить("УникальныйИдентификатор", СтарыйПользователь.ИдентификаторПользователяИБ);
		ОписаниеПользователяИБ.Вставить("РезультатДействия", "УдаленПользовательИБ");
		
	ИначеЕсли ЗначениеЗаполнено(СтарыйПользователь.ИдентификаторПользователяИБ) Тогда
		ОписаниеПользователяИБ.Вставить("РезультатДействия", "ОчищеноСопоставлениеСНесуществующимПользователемИБ");
	Иначе
		ОписаниеПользователяИБ.Вставить("РезультатДействия", "НеТребуетсяУдалениеПользовательИБ");
	КонецЕсли;
	
КонецПроцедуры

Процедура ЗаписатьСвойствоПотребоватьСменуПароляПриВходе(Пользователь, Значение)
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.СведенияОПользователях");
	ЭлементБлокировки.УстановитьЗначение("Пользователь", Пользователь);
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		НаборЗаписей = РегистрыСведений.СведенияОПользователях.СоздатьНаборЗаписей();
		НаборЗаписей.Отбор.Пользователь.Установить(Пользователь);
		НаборЗаписей.Прочитать();
		Если НаборЗаписей.Количество() = 0 Тогда
			СведенияОПользователе = НаборЗаписей.Добавить();
			СведенияОПользователе.Пользователь = Пользователь;
		Иначе
			СведенияОПользователе = НаборЗаписей[0];
		КонецЕсли;
		СведенияОПользователе.ПотребоватьСменуПароляПриВходе = Значение;
		НаборЗаписей.Записать();
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Для процедуры ЗавершитьОбработкуПользователяИБ.

Процедура ПроверитьИзмененияРеквизитовПользователя(ПользовательОбъект, ПараметрыОбработки)
	
	СтарыйПользователь   = ПараметрыОбработки.СтарыйПользователь;
	АвтоРеквизиты        = ПараметрыОбработки.АвтоРеквизиты;
	БлокируемыеРеквизиты = ПараметрыОбработки.БлокируемыеРеквизиты;
	
	Если ТипЗнч(ПользовательОбъект) = Тип("СправочникОбъект.Пользователи")
	   И БлокируемыеРеквизиты.Служебный <> ПользовательОбъект.Служебный Тогда
		
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка при записи пользователя ""%1"".
			           |Реквизит Служебный не допускается изменять в подписках на события.'"),
			ПользовательОбъект.Ссылка);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если БлокируемыеРеквизиты.Подготовлен <> ПользовательОбъект.Подготовлен Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка при записи пользователя ""%1"".
			           |Реквизит Подготовлен не допускается изменять в подписках на события.'"),
			ПользовательОбъект.Ссылка);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если АвтоРеквизиты.ИдентификаторПользователяИБ <> ПользовательОбъект.ИдентификаторПользователяИБ Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка при записи пользователя ""%1"".
			           |Реквизит %2 не допускается изменять.
			           |Обновление реквизита выполняется автоматически.'"),
			ПользовательОбъект.Ссылка,
			"ИдентификаторПользователяИБ");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если Не ОбщегоНазначения.ДанныеСовпадают(АвтоРеквизиты.УдалитьСвойстваПользователяИБ,
				ПользовательОбъект.УдалитьСвойстваПользователяИБ) Тогда
		
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка при записи пользователя ""%1"".
			           |Реквизит %2 не допускается изменять.
			           |Обновление реквизита выполняется автоматически.'"),
			ПользовательОбъект.Ссылка,
			"УдалитьСвойстваПользователяИБ");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	Если СтарыйПользователь.ПометкаУдаления = Ложь
	   И ПользовательОбъект.ПометкаУдаления = Истина
	   И Пользователи.ВходВПрограммуРазрешен(ПользовательОбъект.ИдентификаторПользователяИБ) Тогда
		
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка при записи пользователя ""%1"".
			           |Нельзя помечать на удаление пользователя, которому разрешен вход в программу.'"),
			ПользовательОбъект.Ссылка);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если СтарыйПользователь.Недействителен = Ложь
	   И ПользовательОбъект.Недействителен = Истина
	   И Пользователи.ВходВПрограммуРазрешен(ПользовательОбъект.ИдентификаторПользователяИБ) Тогда
		
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка при записи пользователя ""%1"".
			           |Нельзя пометить недействительным пользователя, которому разрешен вход в программу.'"),
			ПользовательОбъект.Ссылка);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если СтарыйПользователь.Подготовлен = Ложь
	   И ПользовательОбъект.Подготовлен = Истина
	   И Пользователи.ВходВПрограммуРазрешен(ПользовательОбъект.ИдентификаторПользователяИБ) Тогда
		
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка при записи пользователя ""%1"".
			           |Нельзя пометить подготовленным пользователя, которому разрешен вход в программу.'"),
			ПользовательОбъект.Ссылка);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
КонецПроцедуры

Процедура ОбновитьСведенияОПользователеСРазрешеннымВходом(Пользователь, ВходВПрограммуВключен)
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.СведенияОПользователях");
	ЭлементБлокировки.УстановитьЗначение("Пользователь", Пользователь);
	
	НаборЗаписей = РегистрыСведений.СведенияОПользователях.СоздатьНаборЗаписей();
	НаборЗаписей.Отбор.Пользователь.Установить(Пользователь);
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		НаборЗаписей.Прочитать();
		Если НаборЗаписей.Количество() = 0 Тогда
			НаборЗаписей.Добавить();
			НаборЗаписей[0].Пользователь = Пользователь;
		КонецЕсли;
		Записать = Ложь;
		Если ЗначениеЗаполнено(НаборЗаписей[0].ДатаАвтоматическогоЗапрещенияВхода) Тогда
			Записать = Истина;
			НаборЗаписей[0].ДатаАвтоматическогоЗапрещенияВхода = Неопределено;
		КонецЕсли;
		Если ВходВПрограммуВключен
		   И НаборЗаписей[0].ДатаРазрешенияВходаВПрограмму <> НачалоДня(ТекущаяДатаСеанса()) Тогда
			Записать = Истина;
			НаборЗаписей[0].ДатаРазрешенияВходаВПрограмму = НачалоДня(ТекущаяДатаСеанса());
		КонецЕсли;
		Если Записать Тогда
			НаборЗаписей.Записать();
		КонецЕсли;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

Процедура ОтключитьНеактивныхИПросроченныхПользователей(ТолькоДляАвторизованногоПользователя = Ложь,
			ОписаниеОшибки = "", РегистрироватьВЖурнале = Истина)
	
	Если ОбщегоНазначения.РазделениеВключено()
	 Или Не Пользователи.ОбщиеНастройкиВходаИспользуются() Тогда
		Возврат;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	Настройки = НастройкиВхода();
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ПустаяДата",                                 '00010101');
	Запрос.УстановитьПараметр("НачалоДняТекущейДатыСеанса",                 НачалоДня(ТекущаяДатаСеанса()));
	Запрос.УстановитьПараметр("ДатаВключенияПросрочкиПользователей",        Настройки.Пользователи.ПросрочкаРаботыВПрограммеДатаВключения);
	Запрос.УстановитьПараметр("ПросрочкаРаботыПользователей",               Настройки.Пользователи.ПросрочкаРаботыВПрограммеДоЗапрещенияВхода);
	Запрос.УстановитьПараметр("ДатаВключенияПросрочкиВнешнихПользователей", Настройки.ВнешниеПользователи.ПросрочкаРаботыВПрограммеДатаВключения);
	Запрос.УстановитьПараметр("ПросрочкаРаботыВнешнихПользователей",        Настройки.ВнешниеПользователи.ПросрочкаРаботыВПрограммеДоЗапрещенияВхода);
	
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Пользователи.Ссылка КАК Пользователь,
	|	ВЫБОР
	|		КОГДА ЕСТЬNULL(СведенияОПользователях.СрокДействия, &ПустаяДата) <> &ПустаяДата
	|			ТОГДА ЕСТЬNULL(СведенияОПользователях.СрокДействия, &ПустаяДата) <= &НачалоДняТекущейДатыСеанса
	|		ИНАЧЕ ЛОЖЬ
	|	КОНЕЦ КАК СрокДействияИстек
	|ИЗ
	|	Справочник.Пользователи КАК Пользователи
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СведенияОПользователях КАК СведенияОПользователях
	|		ПО (СведенияОПользователях.Пользователь = Пользователи.Ссылка)
	|ГДЕ
	|	&ОтборПользователей
	|	И ЕСТЬNULL(СведенияОПользователях.СрокДействияНеОграничен, ЛОЖЬ) = ЛОЖЬ
	|	И ЕСТЬNULL(СведенияОПользователях.ДатаАвтоматическогоЗапрещенияВхода, &ПустаяДата) = &ПустаяДата
	|	И ВЫБОР
	|			КОГДА ЕСТЬNULL(СведенияОПользователях.СрокДействия, &ПустаяДата) <> &ПустаяДата
	|				ТОГДА ЕСТЬNULL(СведенияОПользователях.СрокДействия, &ПустаяДата) <= &НачалоДняТекущейДатыСеанса
	|			КОГДА ЕСТЬNULL(СведенияОПользователях.ПросрочкаРаботыВПрограммеДоЗапрещенияВхода, 0) <> 0
	|				ТОГДА ВЫБОР
	|						КОГДА ЕСТЬNULL(СведенияОПользователях.ДатаПоследнейАктивности, &ПустаяДата) <= ЕСТЬNULL(СведенияОПользователях.ДатаРазрешенияВходаВПрограмму, &ПустаяДата)
	|							ТОГДА ВЫБОР
	|									КОГДА ЕСТЬNULL(СведенияОПользователях.ДатаРазрешенияВходаВПрограмму, &ПустаяДата) = &ПустаяДата
	|										ТОГДА &НачалоДняТекущейДатыСеанса > ДОБАВИТЬКДАТЕ(&ДатаВключенияПросрочкиПользователей, ДЕНЬ, СведенияОПользователях.ПросрочкаРаботыВПрограммеДоЗапрещенияВхода)
	|									ИНАЧЕ &НачалоДняТекущейДатыСеанса > ДОБАВИТЬКДАТЕ(СведенияОПользователях.ДатаРазрешенияВходаВПрограмму, ДЕНЬ, СведенияОПользователях.ПросрочкаРаботыВПрограммеДоЗапрещенияВхода)
	|								КОНЕЦ
	|						КОГДА &НачалоДняТекущейДатыСеанса > ДОБАВИТЬКДАТЕ(ЕСТЬNULL(СведенияОПользователях.ДатаПоследнейАктивности, &ПустаяДата), ДЕНЬ, СведенияОПользователях.ПросрочкаРаботыВПрограммеДоЗапрещенияВхода)
	|							ТОГДА ИСТИНА
	|						ИНАЧЕ ЛОЖЬ
	|					КОНЕЦ
	|			ИНАЧЕ ВЫБОР
	|					КОГДА &ПросрочкаРаботыПользователей = 0
	|						ТОГДА ЛОЖЬ
	|					КОГДА ЕСТЬNULL(СведенияОПользователях.ДатаПоследнейАктивности, &ПустаяДата) <= ЕСТЬNULL(СведенияОПользователях.ДатаРазрешенияВходаВПрограмму, &ПустаяДата)
	|						ТОГДА ВЫБОР
	|								КОГДА ЕСТЬNULL(СведенияОПользователях.ДатаРазрешенияВходаВПрограмму, &ПустаяДата) = &ПустаяДата
	|									ТОГДА &НачалоДняТекущейДатыСеанса > ДОБАВИТЬКДАТЕ(&ДатаВключенияПросрочкиПользователей, ДЕНЬ, &ПросрочкаРаботыПользователей)
	|								ИНАЧЕ &НачалоДняТекущейДатыСеанса > ДОБАВИТЬКДАТЕ(СведенияОПользователях.ДатаРазрешенияВходаВПрограмму, ДЕНЬ, &ПросрочкаРаботыПользователей)
	|							КОНЕЦ
	|					КОГДА &НачалоДняТекущейДатыСеанса > ДОБАВИТЬКДАТЕ(ЕСТЬNULL(СведенияОПользователях.ДатаПоследнейАктивности, &ПустаяДата), ДЕНЬ, &ПросрочкаРаботыПользователей)
	|						ТОГДА ИСТИНА
	|					ИНАЧЕ ЛОЖЬ
	|				КОНЕЦ
	|		КОНЕЦ
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ
	|	ВнешниеПользователи.Ссылка,
	|	ВЫБОР
	|		КОГДА ЕСТЬNULL(СведенияОПользователях.СрокДействия, &ПустаяДата) <> &ПустаяДата
	|			ТОГДА ЕСТЬNULL(СведенияОПользователях.СрокДействия, &ПустаяДата) <= &НачалоДняТекущейДатыСеанса
	|		ИНАЧЕ ЛОЖЬ
	|	КОНЕЦ
	|ИЗ
	|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СведенияОПользователях КАК СведенияОПользователях
	|		ПО (СведенияОПользователях.Пользователь = ВнешниеПользователи.Ссылка)
	|ГДЕ
	|	&ОтборВнешнихПользователей
	|	И ЕСТЬNULL(СведенияОПользователях.СрокДействияНеОграничен, ЛОЖЬ) = ЛОЖЬ
	|	И ЕСТЬNULL(СведенияОПользователях.ДатаАвтоматическогоЗапрещенияВхода, &ПустаяДата) = &ПустаяДата
	|	И ВЫБОР
	|			КОГДА ЕСТЬNULL(СведенияОПользователях.СрокДействия, &ПустаяДата) <> &ПустаяДата
	|				ТОГДА ЕСТЬNULL(СведенияОПользователях.СрокДействия, &ПустаяДата) <= &НачалоДняТекущейДатыСеанса
	|			КОГДА ЕСТЬNULL(СведенияОПользователях.ПросрочкаРаботыВПрограммеДоЗапрещенияВхода, 0) <> 0
	|				ТОГДА ВЫБОР
	|						КОГДА ЕСТЬNULL(СведенияОПользователях.ДатаПоследнейАктивности, &ПустаяДата) <= ЕСТЬNULL(СведенияОПользователях.ДатаРазрешенияВходаВПрограмму, &ПустаяДата)
	|							ТОГДА ВЫБОР
	|									КОГДА ЕСТЬNULL(СведенияОПользователях.ДатаРазрешенияВходаВПрограмму, &ПустаяДата) = &ПустаяДата
	|										ТОГДА &НачалоДняТекущейДатыСеанса > ДОБАВИТЬКДАТЕ(&ДатаВключенияПросрочкиВнешнихПользователей, ДЕНЬ, СведенияОПользователях.ПросрочкаРаботыВПрограммеДоЗапрещенияВхода)
	|									ИНАЧЕ &НачалоДняТекущейДатыСеанса > ДОБАВИТЬКДАТЕ(СведенияОПользователях.ДатаРазрешенияВходаВПрограмму, ДЕНЬ, СведенияОПользователях.ПросрочкаРаботыВПрограммеДоЗапрещенияВхода)
	|								КОНЕЦ
	|						КОГДА &НачалоДняТекущейДатыСеанса > ДОБАВИТЬКДАТЕ(ЕСТЬNULL(СведенияОПользователях.ДатаПоследнейАктивности, &ПустаяДата), ДЕНЬ, СведенияОПользователях.ПросрочкаРаботыВПрограммеДоЗапрещенияВхода)
	|							ТОГДА ИСТИНА
	|						ИНАЧЕ ЛОЖЬ
	|					КОНЕЦ
	|			ИНАЧЕ ВЫБОР
	|					КОГДА &ПросрочкаРаботыВнешнихПользователей = 0
	|						ТОГДА ЛОЖЬ
	|					КОГДА ЕСТЬNULL(СведенияОПользователях.ДатаПоследнейАктивности, &ПустаяДата) <= ЕСТЬNULL(СведенияОПользователях.ДатаРазрешенияВходаВПрограмму, &ПустаяДата)
	|						ТОГДА ВЫБОР
	|								КОГДА ЕСТЬNULL(СведенияОПользователях.ДатаРазрешенияВходаВПрограмму, &ПустаяДата) = &ПустаяДата
	|									ТОГДА &НачалоДняТекущейДатыСеанса > ДОБАВИТЬКДАТЕ(&ДатаВключенияПросрочкиВнешнихПользователей, ДЕНЬ, &ПросрочкаРаботыВнешнихПользователей)
	|								ИНАЧЕ &НачалоДняТекущейДатыСеанса > ДОБАВИТЬКДАТЕ(СведенияОПользователях.ДатаРазрешенияВходаВПрограмму, ДЕНЬ, &ПросрочкаРаботыВнешнихПользователей)
	|							КОНЕЦ
	|					КОГДА &НачалоДняТекущейДатыСеанса > ДОБАВИТЬКДАТЕ(ЕСТЬNULL(СведенияОПользователях.ДатаПоследнейАктивности, &ПустаяДата), ДЕНЬ, &ПросрочкаРаботыВнешнихПользователей)
	|						ТОГДА ИСТИНА
	|					ИНАЧЕ ЛОЖЬ
	|				КОНЕЦ
	|		КОНЕЦ";
	Если ТолькоДляАвторизованногоПользователя Тогда
		Запрос.УстановитьПараметр("Пользователь", Пользователи.АвторизованныйПользователь());
		ОтборПользователей        = "Пользователи.Ссылка = &Пользователь";
		ОтборВнешнихПользователей = "ВнешниеПользователи.Ссылка = &Пользователь";
	Иначе
		ОтборПользователей        = "ИСТИНА";
		ОтборВнешнихПользователей = "ИСТИНА";
	КонецЕсли;
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОтборПользователей",        ОтборПользователей);
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОтборВнешнихПользователей", ОтборВнешнихПользователей);
	
	Выборка = Запрос.Выполнить().Выбрать();
	
	ИнформацияОбОшибке = Неопределено;
	Пока Выборка.Следующий() Цикл
		Пользователь = Выборка.Пользователь;
		Если НЕ Выборка.СрокДействияИстек
		   И Пользователи.ЭтоПолноправныйПользователь(Пользователь,, Ложь) Тогда
			Продолжить;
		КонецЕсли;
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.СведенияОПользователях");
		ЭлементБлокировки.УстановитьЗначение("Пользователь", Пользователь);
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			ИдентификаторПользователяИБ = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Пользователь,
				"ИдентификаторПользователяИБ");
			ПользовательИБ = Неопределено;
			Если ТипЗнч(ИдентификаторПользователяИБ) = Тип("УникальныйИдентификатор") Тогда
				ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
					ИдентификаторПользователяИБ);
			КонецЕсли;
			Если ПользовательИБ <> Неопределено
			   И (    ПользовательИБ.АутентификацияСтандартная
			      Или ПользовательИБ.АутентификацияOpenID
			      Или ПользовательИБ.АутентификацияOpenIDConnect
			      Или ПользовательИБ.АутентификацияТокеномДоступа
			      Или ПользовательИБ.АутентификацияОС) Тогда
				
				ОбновляемыеСвойства = Новый Структура;
				ОбновляемыеСвойства.Вставить("АутентификацияСтандартная",    Ложь);
				ОбновляемыеСвойства.Вставить("АутентификацияOpenID",         Ложь);
				ОбновляемыеСвойства.Вставить("АутентификацияOpenIDConnect",  Ложь);
				ОбновляемыеСвойства.Вставить("АутентификацияТокеномДоступа", Ложь);
				ОбновляемыеСвойства.Вставить("АутентификацияОС",             Ложь);
				
				Пользователи.УстановитьСвойстваПользователяИБ(ПользовательИБ.УникальныйИдентификатор,
					ОбновляемыеСвойства, Ложь, ТипЗнч(Пользователь) = Тип("СправочникСсылка.ВнешниеПользователи"));
			КонецЕсли;
			НаборЗаписей = РегистрыСведений.СведенияОПользователях.СоздатьНаборЗаписей();
			НаборЗаписей.Отбор.Пользователь.Установить(Пользователь);
			НаборЗаписей.Прочитать();
			Если НаборЗаписей.Количество() = 0 Тогда
				СведенияОПользователе = НаборЗаписей.Добавить();
				СведенияОПользователе.Пользователь = Пользователь;
			Иначе
				СведенияОПользователе = НаборЗаписей[0];
			КонецЕсли;
			СведенияОПользователе.ДатаАвтоматическогоЗапрещенияВхода = НачалоДня(ТекущаяДатаСеанса());
			НаборЗаписей.Записать();
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			
			ШаблонТекстаОшибки = ШаблонТекстаОшибкиЗаписиСведенийОТекущемПользователе();
			ОписаниеОшибки = ЗаголовокСообщенияАвторизацияНеВыполненаСПереводомСтроки()
				+ СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонТекстаОшибки,
					ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
			
			Если РегистрироватьВЖурнале Тогда
				Если Выборка.СрокДействияИстек Тогда
					ШаблонКомментария =
						НСтр("ru = 'Не удалось снять пользователю ""%1"" признак
						           |""Вход в программу разрешен"" в связи с окончанием срока действия по причине:
						           |%2'");
				Иначе
					ШаблонКомментария =
						НСтр("ru = 'Не удалось снять пользователю ""%1"" признак
						           |""Вход в программу разрешен"" в связи с отсутствием работы
						           |в программе более установленного срока по причине:
						           |%2'");
				КонецЕсли;
				ЗаписьЖурналаРегистрации(
					НСтр("ru = 'Пользователи.Ошибка автоматического запрещения входа в программу'",
					     ОбщегоНазначения.КодОсновногоЯзыка()),
					УровеньЖурналаРегистрации.Ошибка,
					Метаданные.НайтиПоТипу(ТипЗнч(Пользователь)),
					Пользователь,
					СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонКомментария,
						Пользователь, ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)));
			КонецЕсли;
		КонецПопытки;
	КонецЦикла;
	
КонецПроцедуры

Процедура ПроверитьВходВПрограммуРазрешен(ОшибкаАвторизации)
	
	УстановитьПривилегированныйРежим(Истина);
	
	Идентификатор = ПользователиИнформационнойБазы.ТекущийПользователь().УникальныйИдентификатор;
	ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(Идентификатор);
	АвторизованныйПользователь = Пользователи.АвторизованныйПользователь();
	Недействителен = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(АвторизованныйПользователь, "Недействителен");
	
	Если ПользовательИБ = Неопределено
	 Или Не ЗначениеЗаполнено(ПользовательИБ.Имя)
	 Или Пользователи.ВходВПрограммуРазрешен(ПользовательИБ)
	   И (РолиАдминистратораДоступны(ПользовательИБ)
	      Или Недействителен <> Истина) Тогда
			Возврат;
	КонецЕсли;
	
	ОшибкаАвторизации = ЗаголовокСообщенияАвторизацияНеВыполненаСПереводомСтроки()
		+ НСтр("ru = 'Ваша учетная запись отключена. Обратитесь к администратору.'");
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Для процедуры ОбработатьИнтерфейсРолей.

// Заполняет коллекцию ролей.
//
// Параметры:
//  Параметры - см. ОбработатьИнтерфейсРолей.Параметры
//
Процедура ЗаполнитьРоли(Параметры)
	
	ПрочитанныеРоли = Параметры.ОсновнойПараметр;
	КоллекцияРолей  = Параметры.КоллекцияРолей;
	
	КоллекцияРолей.Очистить();
	ДобавленныеРоли = Новый Соответствие;
	
	Если ТипЗнч(ПрочитанныеРоли) = Тип("Массив") Тогда
		Для Каждого Роль Из ПрочитанныеРоли Цикл
			Если ДобавленныеРоли.Получить(Роль) <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			ДобавленныеРоли.Вставить(Роль, Истина);
			КоллекцияРолей.Добавить().Роль = Роль;
		КонецЦикла;
	Иначе
		ИдентификаторыРолей = Новый Массив;
		Для Каждого Строка Из ПрочитанныеРоли Цикл
			Если ТипЗнч(Строка.Роль) = Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных")
			 Или ТипЗнч(Строка.Роль) = Тип("СправочникСсылка.ИдентификаторыОбъектовРасширений") Тогда
				ИдентификаторыРолей.Добавить(Строка.Роль);
			КонецЕсли;
		КонецЦикла;
		ПрочитанныеРоли = ОбщегоНазначения.ОбъектыМетаданныхПоИдентификаторам(ИдентификаторыРолей, Ложь);
		
		Для Каждого ОписаниеРоли Из ПрочитанныеРоли Цикл
			Если ТипЗнч(ОписаниеРоли.Значение) <> Тип("ОбъектМетаданных") Тогда
				Роль = ОписаниеРоли.Ключ;
				ИмяРоли = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Роль, "Имя");
				ИмяРоли = ?(ИмяРоли = Неопределено, "(" + Роль.УникальныйИдентификатор() + ")", ИмяРоли);
				ИмяРоли = ?(Лев(ИмяРоли, 1) = "?", ИмяРоли, "? " + СокрЛ(ИмяРоли));
				КоллекцияРолей.Добавить().Роль = СокрЛП(ИмяРоли);
			Иначе
				КоллекцияРолей.Добавить().Роль = ОписаниеРоли.Значение.Имя;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	ОбновитьДеревоРолей(Параметры);
	
КонецПроцедуры

// Параметры:
//  Параметры - см. ОбработатьИнтерфейсРолей.Параметры
//
Процедура НастроитьИнтерфейсРолейПриСозданииФормы(Параметры)
	
	Форма = Параметры.Форма;
	
	// Условное оформление недоступных ролей.
	ЭлементУсловногоОформления = Форма.УсловноеОформление.Элементы.Добавить();
	
	ЭлементЦветаОформления = ЭлементУсловногоОформления.Оформление.Элементы.Найти("TextColor");
	ЭлементЦветаОформления.Значение = Метаданные.ЭлементыСтиля.ПоясняющийОшибкуТекст.Значение;
	ЭлементЦветаОформления.Использование = Истина;
	
	ЭлементОтбораДанных = ЭлементУсловногоОформления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	ЭлементОтбораДанных.ЛевоеЗначение  = Новый ПолеКомпоновкиДанных("Роли.ЭтоНедоступнаяРоль");
	ЭлементОтбораДанных.ВидСравнения   = ВидСравненияКомпоновкиДанных.Равно;
	ЭлементОтбораДанных.ПравоеЗначение = Истина;
	ЭлементОтбораДанных.Использование  = Истина;
	
	ЭлементОформляемогоПоля = ЭлементУсловногоОформления.Поля.Элементы.Добавить();
	ЭлементОформляемогоПоля.Поле = Новый ПолеКомпоновкиДанных("Роли");
	ЭлементОформляемогоПоля.Использование = Истина;
	
	// Условное оформление несуществующих ролей.
	ЭлементУсловногоОформления = Форма.УсловноеОформление.Элементы.Добавить();
	
	ЭлементЦветаОформления = ЭлементУсловногоОформления.Оформление.Элементы.Найти("TextColor");
	ЭлементЦветаОформления.Значение = Метаданные.ЭлементыСтиля.ТекстЗапрещеннойЯчейкиЦвет.Значение;
	ЭлементЦветаОформления.Использование = Истина;
	
	ЭлементОтбораДанных = ЭлементУсловногоОформления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	ЭлементОтбораДанных.ЛевоеЗначение  = Новый ПолеКомпоновкиДанных("Роли.ЭтоНесуществующаяРоль");
	ЭлементОтбораДанных.ВидСравнения   = ВидСравненияКомпоновкиДанных.Равно;
	ЭлементОтбораДанных.ПравоеЗначение = Истина;
	ЭлементОтбораДанных.Использование  = Истина;
	
	ЭлементОформляемогоПоля = ЭлементУсловногоОформления.Поля.Элементы.Добавить();
	ЭлементОформляемогоПоля.Поле = Новый ПолеКомпоновкиДанных("Роли");
	ЭлементОформляемогоПоля.Использование = Истина;
	
	НастроитьИнтерфейсРолейПриЧтенииНаСервере(Параметры);
	
КонецПроцедуры

// Параметры:
//  Параметры - см. ОбработатьИнтерфейсРолей.Параметры
//
Процедура НастроитьИнтерфейсРолейПриЧтенииНаСервере(Параметры)
	
	Форма    = Параметры.Форма;
	Элементы = Форма.Элементы;
	
	// Установка начальных значений перед загрузкой данных из настроек на сервере
	// для случая, когда данные еще не были записаны и не загружаются.
	Форма.ПоказатьПодсистемыРолей = Ложь;
	Элементы.РолиПоказатьПодсистемыРолей.Пометка = Ложь;
	
	// Для нового элемента показать все роли, для существующего - только выбранные роли.
	Если Элементы.Найти("РолиПоказатьТолькоВыбранныеРоли") <> Неопределено Тогда
		Элементы.РолиПоказатьТолькоВыбранныеРоли.Пометка = Параметры.ОсновнойПараметр;
	КонецЕсли;
	
	ОбновитьДеревоРолей(Параметры);
	
КонецПроцедуры

// Параметры:
//  Параметры - см. ОбработатьИнтерфейсРолей.Параметры
//
Процедура НастроитьИнтерфейсРолейПриЗагрузкеНастроек(Параметры)
	
	Настройки = Параметры.ОсновнойПараметр;
	Форма     = Параметры.Форма;
	Элементы  = Форма.Элементы;
	
	ПоказатьПодсистемыРолей = Форма.ПоказатьПодсистемыРолей;
	
	Если Настройки["ПоказатьПодсистемыРолей"] = Ложь Тогда
		Форма.ПоказатьПодсистемыРолей = Ложь;
		Элементы.РолиПоказатьПодсистемыРолей.Пометка = Ложь;
	Иначе
		Форма.ПоказатьПодсистемыРолей = Истина;
		Элементы.РолиПоказатьПодсистемыРолей.Пометка = Истина;
	КонецЕсли;
	
	Если ПоказатьПодсистемыРолей <> Форма.ПоказатьПодсистемыРолей Тогда
		ОбновитьДеревоРолей(Параметры);
	КонецЕсли;
	
КонецПроцедуры

// Параметры:
//  Параметры - см. ОбработатьИнтерфейсРолей.Параметры
//
Процедура УстановитьТолькоПросмотрРолей(Параметры)
	
	Форма = Параметры.Форма;
	Элементы               = Форма.Элементы;
	ТолькоПросмотрРолей    = Параметры.ОсновнойПараметр;
	
	Если ТолькоПросмотрРолей <> Неопределено Тогда
		
		Элементы.Роли.ТолькоПросмотр = ТолькоПросмотрРолей;
		
		НайденныйЭлемент = Элементы.Найти("РолиУстановитьФлажки"); // КнопкаФормы
		Если НайденныйЭлемент <> Неопределено Тогда
			НайденныйЭлемент.Доступность = НЕ ТолькоПросмотрРолей;
		КонецЕсли;
		
		НайденныйЭлемент = Элементы.Найти("РолиСнятьФлажки"); // КнопкаФормы
		Если НайденныйЭлемент <> Неопределено Тогда
			НайденныйЭлемент.Доступность = НЕ ТолькоПросмотрРолей;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Параметры:
//  Параметры - см. ОбработатьИнтерфейсРолей.Параметры
//
Процедура ТолькоВыбранныеРоли(Параметры)
	
	Форма = Параметры.Форма;
	
	Форма.Элементы.РолиПоказатьТолькоВыбранныеРоли.Пометка =
		НЕ Форма.Элементы.РолиПоказатьТолькоВыбранныеРоли.Пометка;
	
	ОбновитьДеревоРолей(Параметры);
	
КонецПроцедуры

// Параметры:
//  Параметры - см. ОбработатьИнтерфейсРолей.Параметры
//
Процедура ГруппировкаПоПодсистемам(Параметры)
	
	Форма = Параметры.Форма;
	
	Форма.ПоказатьПодсистемыРолей = НЕ Параметры.Форма.ПоказатьПодсистемыРолей;
	Форма.Элементы.РолиПоказатьПодсистемыРолей.Пометка = Параметры.Форма.ПоказатьПодсистемыРолей;
	
	ОбновитьДеревоРолей(Параметры);
	
КонецПроцедуры

// Параметры:
//  Параметры - см. ОбработатьИнтерфейсРолей.Параметры
//
Процедура ОбновитьДеревоРолей(Параметры)
	
	Форма           = Параметры.Форма;
	Элементы        = Форма.Элементы;
	Роли            = Форма.Роли;
	НазначениеРолей = Параметры.НазначениеРолей;
	
	СкрытьРольПолныеПрава = Параметры.Свойство("СкрытьРольПолныеПрава")
	                      И Параметры.СкрытьРольПолныеПрава = Истина;
	
	Если Элементы.Найти("РолиПоказатьТолькоВыбранныеРоли") <> Неопределено Тогда
		Если НЕ Элементы.РолиПоказатьТолькоВыбранныеРоли.Доступность Тогда
			Элементы.РолиПоказатьТолькоВыбранныеРоли.Пометка = Истина;
		КонецЕсли;
		ПоказатьТолькоВыбранныеРоли = Элементы.РолиПоказатьТолькоВыбранныеРоли.Пометка;
	Иначе
		ПоказатьТолькоВыбранныеРоли = Истина;
	КонецЕсли;
	
	ПоказатьПодсистемыРолей = Параметры.Форма.ПоказатьПодсистемыРолей;
	
	// Запоминание текущей строки.
	ТекущаяПодсистема = "";
	ТекущаяРоль       = "";
	
	Если Элементы.Роли.ТекущаяСтрока <> Неопределено Тогда
		ТекущиеДанные = Роли.НайтиПоИдентификатору(Элементы.Роли.ТекущаяСтрока);
		
		Если ТекущиеДанные = Неопределено Тогда
			Элементы.Роли.ТекущаяСтрока = Неопределено;
			
		ИначеЕсли ТекущиеДанные.ЭтоРоль Тогда
			ТекущаяРоль       = ТекущиеДанные.Имя;
			ТекущаяПодсистема = ?(ТекущиеДанные.ПолучитьРодителя() = Неопределено, "",
				ТекущиеДанные.ПолучитьРодителя().Имя);
		Иначе
			ТекущаяРоль       = "";
			ТекущаяПодсистема = ТекущиеДанные.Имя;
		КонецЕсли;
	КонецЕсли;
	
	ХранилищеДереваРолей = ПользователиСлужебныйПовтИсп.ДеревоРолей(ПоказатьПодсистемыРолей, НазначениеРолей);
	ДеревоРолей = ХранилищеДереваРолей.Получить(); // см. ПользователиСлужебныйПовтИсп.ДеревоРолей
	
	ДеревоРолей.Колонки.Добавить("ЭтоНедоступнаяРоль",    Новый ОписаниеТипов("Булево"));
	ДеревоРолей.Колонки.Добавить("ЭтоНесуществующаяРоль", Новый ОписаниеТипов("Булево"));
	ДобавитьИменаНесуществующихИНедоступныхРолей(Параметры, ДеревоРолей);
	
	ДеревоРолей.Колонки.Добавить("Пометка",       Новый ОписаниеТипов("Булево"));
	ДеревоРолей.Колонки.Добавить("НомерКартинки", Новый ОписаниеТипов("Число"));
	ПодготовитьДеревоРолей(ДеревоРолей.Строки, СкрытьРольПолныеПрава, ПоказатьТолькоВыбранныеРоли,
		Параметры.КоллекцияРолей, ?(Параметры.Свойство("СтандартныеРолиРасширений"),
			Параметры.СтандартныеРолиРасширений, Неопределено));
	
	Параметры.Форма.ЗначениеВРеквизитФормы(ДеревоРолей, "Роли");
	
	Элементы.Роли.Отображение = ?(ДеревоРолей.Строки.Найти(Ложь, "ЭтоРоль") = Неопределено,
		ОтображениеТаблицы.Список, ОтображениеТаблицы.Дерево);
	
	// Восстановление текущей строки.
	Отбор = Новый Структура("ЭтоРоль, Имя", Ложь, ТекущаяПодсистема);
	НайденныеСтроки = ДеревоРолей.Строки.НайтиСтроки(Отбор, Истина);
	Если НайденныеСтроки.Количество() <> 0 Тогда
		ОписаниеПодсистемы = НайденныеСтроки[0];
		
		ИндексПодсистемы = ?(ОписаниеПодсистемы.Родитель = Неопределено,
			ДеревоРолей.Строки, ОписаниеПодсистемы.Родитель.Строки).Индекс(ОписаниеПодсистемы);
		
		СтрокаПодсистемы = ДанныеФормыКоллекцияЭлементовДерева(Роли,
			ОписаниеПодсистемы).Получить(ИндексПодсистемы);
		
		Если ЗначениеЗаполнено(ТекущаяРоль) Тогда
			Отбор = Новый Структура("ЭтоРоль, Имя", Истина, ТекущаяРоль);
			НайденныеСтроки = ОписаниеПодсистемы.Строки.НайтиСтроки(Отбор);
			Если НайденныеСтроки.Количество() <> 0 Тогда
				ОписаниеРоли = НайденныеСтроки[0];
				Элементы.Роли.ТекущаяСтрока = СтрокаПодсистемы.ПолучитьЭлементы().Получить(
					ОписаниеПодсистемы.Строки.Индекс(ОписаниеРоли)).ПолучитьИдентификатор();
			Иначе
				Элементы.Роли.ТекущаяСтрока = СтрокаПодсистемы.ПолучитьИдентификатор();
			КонецЕсли;
		Иначе
			Элементы.Роли.ТекущаяСтрока = СтрокаПодсистемы.ПолучитьИдентификатор();
		КонецЕсли;
	Иначе
		Отбор = Новый Структура("ЭтоРоль, Имя", Истина, ТекущаяРоль);
		НайденныеСтроки = ДеревоРолей.Строки.НайтиСтроки(Отбор, Истина);
		Если НайденныеСтроки.Количество() <> 0 Тогда
			ОписаниеРоли = НайденныеСтроки[0];
			
			ИндексРоли = ?(ОписаниеРоли.Родитель = Неопределено,
				ДеревоРолей.Строки, ОписаниеРоли.Родитель.Строки).Индекс(ОписаниеРоли);
			
			СтрокаРоли = ДанныеФормыКоллекцияЭлементовДерева(Роли, ОписаниеРоли).Получить(ИндексРоли);
			Элементы.Роли.ТекущаяСтрока = СтрокаРоли.ПолучитьИдентификатор();
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Параметры:
//  Параметры - см. ОбработатьИнтерфейсРолей.Параметры
//  ДеревоРолей - ДеревоЗначений:
//    * ЭтоРоль - Булево
//    * Имя     - Строка - имя роли или подсистемы.
//    * Синоним - Строка - синоним роли или подсистемы.
//    * ЭтоНедоступнаяРоль    - Булево
//    * ЭтоНесуществующаяРоль - Булево
//    * Пометка               - Булево
//    * НомерКартинки         - Число
//
Процедура ДобавитьИменаНесуществующихИНедоступныхРолей(Параметры, ДеревоРолей)
	
	КоллекцияРолей  = Параметры.КоллекцияРолей;
	ВсеРоли = ВсеРоли().Соответствие;
	
	НедоступныеРоли    = Новый СписокЗначений;
	НесуществующиеРоли = Новый СписокЗначений;
	
	// Добавление несуществующих ролей.
	Для каждого Строка Из КоллекцияРолей Цикл
		Отбор = Новый Структура("ЭтоРоль, Имя", Истина, Строка.Роль);
		Если ДеревоРолей.Строки.НайтиСтроки(Отбор, Истина).Количество() > 0 Тогда
			Продолжить;
		КонецЕсли;
		Синоним = ВсеРоли.Получить(Строка.Роль);
		Если Синоним = Неопределено Тогда
			НесуществующиеРоли.Добавить(Строка.Роль,
				?(Лев(Строка.Роль, 1) = "?", Строка.Роль, "? " + Строка.Роль));
		Иначе
			НедоступныеРоли.Добавить(Строка.Роль, Синоним);
		КонецЕсли;
	КонецЦикла;
	
	НедоступныеРоли.СортироватьПоПредставлению();
	Для Каждого ОписаниеРоли Из НедоступныеРоли Цикл
		Индекс = НедоступныеРоли.Индекс(ОписаниеРоли);
		СтрокаДерева = ДеревоРолей.Строки.Вставить(Индекс);
		СтрокаДерева.Имя     = ОписаниеРоли.Значение;
		СтрокаДерева.Синоним = ОписаниеРоли.Представление;
		СтрокаДерева.ЭтоРоль = Истина;
		СтрокаДерева.ЭтоНедоступнаяРоль = Истина;
	КонецЦикла;
	
	НесуществующиеРоли.СортироватьПоПредставлению();
	Для Каждого ОписаниеРоли Из НесуществующиеРоли Цикл
		Индекс = НесуществующиеРоли.Индекс(ОписаниеРоли);
		СтрокаДерева = ДеревоРолей.Строки.Вставить(Индекс);
		СтрокаДерева.Имя     = ОписаниеРоли.Значение;
		СтрокаДерева.Синоним = ОписаниеРоли.Представление;
		СтрокаДерева.ЭтоРоль = Истина;
		СтрокаДерева.ЭтоНесуществующаяРоль = Истина;
	КонецЦикла;
	
КонецПроцедуры

Процедура ПодготовитьДеревоРолей(Знач Коллекция, Знач СкрытьРольПолныеПрава, Знач ПоказатьТолькоВыбранныеРоли,
			КоллекцияРолей, СтандартныеРолиРасширений)
	
	Индекс = Коллекция.Количество()-1;
	
	Пока Индекс >= 0 Цикл
		Строка = Коллекция[Индекс];
		
		ПодготовитьДеревоРолей(Строка.Строки, СкрытьРольПолныеПрава, ПоказатьТолькоВыбранныеРоли,
			КоллекцияРолей, СтандартныеРолиРасширений);
		
		Если Строка.ЭтоРоль Тогда
			Если СкрытьРольПолныеПрава
			   И (    ВРег(Строка.Имя) = ВРег("ПолныеПрава")
			      ИЛИ ВРег(Строка.Имя) = ВРег("АдминистраторСистемы")
			      ИЛИ СтандартныеРолиРасширений <> Неопределено
			        И (    СтандартныеРолиРасширений.ПолныеПрава.Найти(Строка.Имя) <> Неопределено
			           Или СтандартныеРолиРасширений.АдминистраторСистемы.Найти(Строка.Имя) <> Неопределено)) Тогда
				Коллекция.Удалить(Индекс);
			Иначе
				Строка.НомерКартинки = 7;
				Строка.Пометка = КоллекцияРолей.НайтиСтроки(
					Новый Структура("Роль", Строка.Имя)).Количество() > 0;
				
				Если ПоказатьТолькоВыбранныеРоли И НЕ Строка.Пометка Тогда
					Коллекция.Удалить(Индекс);
				КонецЕсли;
			КонецЕсли;
		Иначе
			Если Строка.Строки.Количество() = 0 Тогда
				Коллекция.Удалить(Индекс);
			Иначе
				Строка.НомерКартинки = 6;
				Строка.Пометка = Строка.Строки.НайтиСтроки(
					Новый Структура("Пометка", Ложь)).Количество() = 0;
			КонецЕсли;
		КонецЕсли;
		
		Индекс = Индекс-1;
	КонецЦикла;
	
КонецПроцедуры

// Возвращает иерархическую коллекцию.
// 
// Параметры:
//  ДанныеФормыДерево - ДанныеФормыДерево
//  СтрокаДереваЗначений - СтрокаДереваЗначений
//
// Возвращаемое значение:
//  ДанныеФормыКоллекцияЭлементовДерева
// 
Функция ДанныеФормыКоллекцияЭлементовДерева(Знач ДанныеФормыДерево, Знач СтрокаДереваЗначений)
	
	Если СтрокаДереваЗначений.Родитель = Неопределено Тогда
		ДанныеФормыКоллекцияЭлементовДерева = ДанныеФормыДерево.ПолучитьЭлементы();
	Иначе
		ИндексРодителя = ?(СтрокаДереваЗначений.Родитель.Родитель = Неопределено,
			СтрокаДереваЗначений.Владелец().Строки, СтрокаДереваЗначений.Родитель.Родитель.Строки).Индекс(
				СтрокаДереваЗначений.Родитель);
			
		ДанныеФормыКоллекцияЭлементовДерева = ДанныеФормыКоллекцияЭлементовДерева(ДанныеФормыДерево,
			СтрокаДереваЗначений.Родитель).Получить(ИндексРодителя).ПолучитьЭлементы();
	КонецЕсли;
	
	Возврат ДанныеФормыКоллекцияЭлементовДерева;
	
КонецФункции

// Параметры:
//  Параметры - см. ОбработатьИнтерфейсРолей.Параметры
//
Процедура ОбновитьСоставРолей(Параметры)
	
	Форма = Параметры.Форма;
	
	Роли                        = Форма.Роли;
	ПоказатьТолькоВыбранныеРоли = Форма.Элементы.РолиПоказатьТолькоВыбранныеРоли.Пометка;
	НазначениеРолей             = Параметры.НазначениеРолей;
	
	ВсеРоли         = ВсеРоли().Массив;
	НедоступныеРоли = ПользователиСлужебныйПовтИсп.НедоступныеРоли(НазначениеРолей);
	
	Если Параметры.ОсновнойПараметр = "ВключитьВсе" Тогда
		ИдентификаторСтроки = Неопределено;
		Добавить            = Истина;
		
	ИначеЕсли Параметры.ОсновнойПараметр = "ИсключитьВсе" Тогда
		ИдентификаторСтроки = Неопределено;
		Добавить            = Ложь;
	Иначе
		ИдентификаторСтроки = Форма.Элементы.Роли.ТекущаяСтрока;
	КонецЕсли;
	
	Если ИдентификаторСтроки = Неопределено Тогда
		
		БылУстановленАдминистративныйДоступ = Параметры.КоллекцияРолей.НайтиСтроки(
			Новый Структура("Роль", "ПолныеПрава")).Количество() > 0;
		
		// Обработка всех.
		КоллекцияРолей = Параметры.КоллекцияРолей;
		КоллекцияРолей.Очистить();
		Если Добавить Тогда
			Для Каждого ИмяРоли Из ВсеРоли Цикл
				
				Если ИмяРоли = "ПолныеПрава"
				 Или ИмяРоли = "АдминистраторСистемы"
				 Или НедоступныеРоли.Получить(ИмяРоли) <> Неопределено
				 Или ВРег(Лев(ИмяРоли, СтрДлина("Удалить"))) = ВРег("Удалить") Тогда
					
					Продолжить;
				КонецЕсли;
				КоллекцияРолей.Добавить().Роль = ИмяРоли;
			КонецЦикла;
		КонецЕсли;
		
		Если Параметры.Свойство("ЗапретИзмененияАдминистративногоДоступа")
			И Параметры.ЗапретИзмененияАдминистративногоДоступа Тогда
			
			УстановленАдминистративныйДоступ = Параметры.КоллекцияРолей.НайтиСтроки(
				Новый Структура("Роль", "ПолныеПрава")).Количество() > 0;
			
			Если УстановленАдминистративныйДоступ И НЕ БылУстановленАдминистративныйДоступ Тогда
				Отбор = Новый Структура("Роль", "ПолныеПрава");
				Параметры.КоллекцияРолей.НайтиСтроки(Отбор).Удалить(0);
				
			ИначеЕсли БылУстановленАдминистративныйДоступ И НЕ УстановленАдминистративныйДоступ Тогда
				КоллекцияРолей.Добавить().Роль = "ПолныеПрава";
			КонецЕсли;
		КонецЕсли;
		ЗаполнитьСтандартныеРолиРасширений(Параметры);
		
		Если ПоказатьТолькоВыбранныеРоли Тогда
			Если КоллекцияРолей.Количество() > 0 Тогда
				ОбновитьДеревоРолей(Параметры);
			Иначе
				Роли.ПолучитьЭлементы().Очистить();
			КонецЕсли;
			
			Возврат;
		КонецЕсли;
	Иначе
		ТекущиеДанные = Роли.НайтиПоИдентификатору(ИдентификаторСтроки);
		Если ТекущиеДанные.ЭтоРоль Тогда
			ДобавитьУдалитьРоль(Параметры, ТекущиеДанные.Имя, ТекущиеДанные.Пометка);
		Иначе
			ДобавитьУдалитьРолиПодсистемы(Параметры, ТекущиеДанные.ПолучитьЭлементы(), ТекущиеДанные.Пометка);
		КонецЕсли;
		ЗаполнитьСтандартныеРолиРасширений(Параметры);
	КонецЕсли;
	
	ОбновитьПометкуВыбранныхРолей(Параметры, Роли.ПолучитьЭлементы());
	
КонецПроцедуры

Процедура ЗаполнитьСтандартныеРолиРасширений(Параметры)
	
	Если Не Параметры.Свойство("СтандартныеРолиРасширений") Тогда
		Возврат;
	КонецЕсли;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом") Тогда
		МодульПрофилиГруппДоступа = ОбщегоНазначения.ОбщийМодуль("Справочники.ПрофилиГруппДоступа");
		МодульПрофилиГруппДоступа.ЗаполнитьСтандартныеРолиРасширений(Параметры.КоллекцияРолей,
			Параметры.СтандартныеРолиРасширений);
	КонецЕсли;
	
КонецПроцедуры

// Параметры:
//  Параметры - см. ОбработатьИнтерфейсРолей.Параметры
//  Роль      - Строка
//  Добавить  - Булево
//
Процедура ДобавитьУдалитьРоль(Параметры, Знач Роль, Знач Добавить)
	
	НайденныеРоли = Параметры.КоллекцияРолей.НайтиСтроки(Новый Структура("Роль", Роль));
	
	Если Добавить Тогда
		Если НайденныеРоли.Количество() = 0 Тогда
			Параметры.КоллекцияРолей.Добавить().Роль = Роль;
		КонецЕсли;
	Иначе
		Если НайденныеРоли.Количество() > 0 Тогда
			Параметры.КоллекцияРолей.Удалить(НайденныеРоли[0]);
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Изменяет состав ролей.
//
// Параметры:
//  Параметры - см. ОбработатьИнтерфейсРолей.Параметры
//  Коллекция - см. ОбработатьИнтерфейсРолей.Параметры.Форма.Элементы.Роли
//  Добавить  - Булево
//
Процедура ДобавитьУдалитьРолиПодсистемы(Параметры, Знач Коллекция, Знач Добавить)
	
	Для каждого Строка Из Коллекция Цикл
		Если Строка.ЭтоРоль Тогда
			ДобавитьУдалитьРоль(Параметры, Строка.Имя, Добавить);
		Иначе
			ДобавитьУдалитьРолиПодсистемы(Параметры, Строка.ПолучитьЭлементы(), Добавить);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Параметры:
//  Параметры - см. ОбработатьИнтерфейсРолей.Параметры
//  Коллекция - см. ОбработатьИнтерфейсРолей.Параметры.Форма.Элементы.Роли
//
Процедура ОбновитьПометкуВыбранныхРолей(Параметры, Знач Коллекция)
	
	Форма = Параметры.Форма;
	
	ПоказатьТолькоВыбранныеРоли = Форма.Элементы.РолиПоказатьТолькоВыбранныеРоли.Пометка;
	
	Индекс = Коллекция.Количество()-1;
	
	Пока Индекс >= 0 Цикл
		Строка = Коллекция[Индекс];
		
		Если Строка.ЭтоРоль Тогда
			Отбор = Новый Структура("Роль", Строка.Имя);
			Строка.Пометка = Параметры.КоллекцияРолей.НайтиСтроки(Отбор).Количество() > 0;
			Если ПоказатьТолькоВыбранныеРоли И НЕ Строка.Пометка Тогда
				Коллекция.Удалить(Индекс);
			КонецЕсли;
		Иначе
			ОбновитьПометкуВыбранныхРолей(Параметры, Строка.ПолучитьЭлементы());
			Если Строка.ПолучитьЭлементы().Количество() = 0 Тогда
				Коллекция.Удалить(Индекс);
			Иначе
				Строка.Пометка = Истина;
				Для каждого Элемент Из Строка.ПолучитьЭлементы() Цикл
					Если НЕ Элемент.Пометка Тогда
						Строка.Пометка = Ложь;
						Прервать;
					КонецЕсли;
				КонецЦикла;
			КонецЕсли;
		КонецЕсли;
		
		Индекс = Индекс-1;
	КонецЦикла;
	
КонецПроцедуры

Функция ПользователиДобавленныеСПомощьюКонфигуратора()
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Пользователи.Ссылка КАК Ссылка,
	|	Пользователи.Наименование КАК ПолноеИмя,
	|	Пользователи.ИдентификаторПользователяИБ,
	|	ЛОЖЬ КАК ЭтоВнешнийПользователь
	|ИЗ
	|	Справочник.Пользователи КАК Пользователи
	|ГДЕ
	|	Пользователи.ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ
	|	ВнешниеПользователи.Ссылка,
	|	ВнешниеПользователи.Наименование,
	|	ВнешниеПользователи.ИдентификаторПользователяИБ,
	|	ИСТИНА
	|ИЗ
	|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
	|ГДЕ
	|	ВнешниеПользователи.ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор";
	
	Запрос.УстановитьПараметр("ПустойУникальныйИдентификатор", 
		ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
	
	Выгрузка = Запрос.Выполнить().Выгрузить();
	Выгрузка.Индексы.Добавить("ИдентификаторПользователяИБ");
	
	ПользователиИБ = ПользователиИнформационнойБазы.ПолучитьПользователей();
	ПользователейДобавленоВКонфигураторе = 0;
	
	Для Каждого ПользовательИБ Из ПользователиИБ Цикл
		
		Строка = Выгрузка.Найти(ПользовательИБ.УникальныйИдентификатор, "ИдентификаторПользователяИБ");
		Если Строка = Неопределено Тогда
			ПользователейДобавленоВКонфигураторе = ПользователейДобавленоВКонфигураторе + 1;
		КонецЕсли;
		
	КонецЦикла;
	
	Возврат ПользователейДобавленоВКонфигураторе;
	
КонецФункции

#КонецОбласти